Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Aug 2010 06:37:02 +0000 (23:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Aug 2010 06:37:02 +0000 (23:37 -0700)
* maint:
t0003: add missing && at end of lines

134 files changed:
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-fast-import.txt
Documentation/git-instaweb.txt
Documentation/git-ls-files.txt
Documentation/git-rebase.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/technical/api-tree-walking.txt
GIT-VERSION-GEN
Makefile
alias.c
bisect.c
branch.c
builtin/blame.c
builtin/checkout.c
builtin/clean.c
builtin/commit.c
builtin/config.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/grep.c
builtin/ls-files.c
builtin/mailsplit.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/push.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
builtin/revert.c
builtin/show-ref.c
cache.h
commit.c
commit.h
contrib/hooks/post-receive-email
date.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
fast-import.c
git-instaweb.sh
git-mergetool.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-svn.perl
git.c
gitweb/gitweb.perl
graph.c
graph.h
http.c
merge-recursive.c
merge-recursive.h
notes.c
object.h
pack-refs.c
path.c
perl/Makefile
reachable.c
refs.c
refs.h
remote.c
rerere.c
resolve-undo.c
revision.c
revision.h
sha1_file.c
shallow.c
string-list.h
submodule.c
submodule.h
t/.gitignore
t/Makefile
t/README
t/lib-rebase.sh
t/t1001-read-tree-m-2way.sh
t/t1011-read-tree-sparse-checkout.sh
t/t2018-checkout-branch.sh [new file with mode: 0755]
t/t3030-merge-recursive.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3306-notes-prune.sh
t/t3400-rebase.sh
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3407-rebase-abort.sh
t/t3410-rebase-preserve-dropped-merges.sh
t/t3505-cherry-pick-empty.sh
t/t4013-diff-various.sh
t/t4013/diff.log_-S_F_master [new file with mode: 0644]
t/t4018-diff-funcname.sh
t/t4027-diff-submodule.sh
t/t4202-log.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t6018-rev-list-glob.sh
t/t6037-merge-ours-theirs.sh
t/t7300-clean.sh
t/t7405-submodule-merge.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7607-merge-overwrite.sh
t/t7609-merge-co-error-msgs.sh [new file with mode: 0755]
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t9100-git-svn-basic.sh
t/t9130-git-svn-authors-file.sh
t/t9139-git-svn-non-utf8-commitencoding.sh
t/t9140-git-svn-reset.sh
t/t9155-git-svn-fetch-deleted-tag.sh [new file with mode: 0755]
t/t9156-git-svn-fetch-deleted-tag-2.sh [new file with mode: 0755]
t/t9300-fast-import.sh
t/test-lib.sh
transport-helper.c
tree-walk.c
tree-walk.h
unpack-trees.c
unpack-trees.h
upload-pack.c
userdiff.c
wt-status.c
index eb53e0636e3c3bab06e88ce3371945f5602c5756..ece3c77482b3ff006b973f1ed90b708e26556862 100644 (file)
@@ -7,17 +7,16 @@ Checklist (and a short version for the impatient):
          before committing
        - do not check in commented out code or unneeded files
        - the first line of the commit message should be a short
-         description and should skip the full stop
+         description (50 characters is the soft limit, see DISCUSSION
+         in git-commit(1)), and should skip the full stop
        - the body should provide a meaningful commit message, which:
                - uses the imperative, present tense: "change",
                  not "changed" or "changes".
                - includes motivation for the change, and contrasts
                  its implementation with previous behaviour
-       - if you want your work included in git.git, add a
-         "Signed-off-by: Your Name <you@example.com>" line to the
-         commit message (or just use the option "-s" when
-         committing) to confirm that you agree to the Developer's
-         Certificate of Origin
+       - add a "Signed-off-by: Your Name <you@example.com>" line to the
+         commit message (or just use the option "-s" when committing)
+         to confirm that you agree to the Developer's Certificate of Origin
        - make sure that you have tests for the bug you are fixing
        - make sure that the test suite passes after your commit
 
index 7fffee7e3de29181479da083b086bf537c6f9354..05ec3fed893b8c179bab2b41f35c34af5ce05b20 100644 (file)
@@ -826,6 +826,11 @@ diff.renames::
        will enable basic rename detection.  If set to "copies" or
        "copy", it will detect copies, as well.
 
+diff.ignoreSubmodules::
+       Sets the default value of --ignore-submodules. Note that this
+       affects only 'git diff' Porcelain, and not lower level 'diff'
+       commands such as 'git diff-files'.
+
 diff.suppressBlankEmpty::
        A boolean to inhibit the standard behavior of printing a space
        before each empty output line. Defaults to false.
@@ -1243,6 +1248,15 @@ http.noEPSV::
        support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
        environment variable. Default is false (curl will use EPSV).
 
+http.useragent::
+       The HTTP USER_AGENT string presented to an HTTP server.  The default
+       value represents the version of the client git such as git/1.7.1.
+       This option allows you to override this value to a more common value
+       such as Mozilla/4.0.  This may be necessary, for instance, if
+       connecting through a firewall that restricts HTTP connections to a set
+       of common USER_AGENT strings (but not including those like git/1.7.1).
+       Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@ -1749,6 +1763,19 @@ submodule.<name>.update::
        URL and other values found in the `.gitmodules` file.  See
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
 
+submodule.<name>.ignore::
+       Defines under what circumstances "git status" and the diff family show
+       a submodule as modified. When set to "all", it will never be considered
+       modified, "dirty" will ignore all changes to the submodules work tree and
+       takes only differences between the HEAD of the submodule and the commit
+       recorded in the superproject into account. "untracked" will additionally
+       let submodules with modified tracked files in their work tree show up.
+       Using "none" (the default when this option is not set) also shows
+       submodules that have untracked files in their work tree as changed.
+       This setting overrides any setting made in .gitmodules for this submodule,
+       both settings can be overridden on the command line by using the
+       "--ignore-submodules" option.
+
 tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index eecedaab6e172f227f2451f4b3d35c9e6e99bf1e..4656a97e6012796f182a725e14c13dc03f71bba2 100644 (file)
@@ -355,7 +355,11 @@ endif::git-format-patch[]
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules in the diff generation. <when> can be
-       either "untracked", "dirty" or "all", which is the default. When
+       either "none", "untracked", "dirty" or "all", which is the default
+       Using "none" will consider the submodule modified when it either contains
+       untracked or modified files or its HEAD differs from the commit recorded
+       in the superproject and can be used to override any settings of the
+       'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
        "untracked" is used submodules are not considered dirty when they only
        contain untracked content (but they are still scanned for modified
        content). Using "dirty" ignores all changes to the work tree of submodules,
index 1bacd2e1044f5b3b7d5a60e1687387cc277fc52a..66e570113a077d2c52c4fc3ac09d24a272df5301 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
 'git checkout' --patch [<tree-ish>] [--] [<paths>...]
 
@@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-'git checkout' -b <new branch> [<start point>]::
+'git checkout' -b|-B <new_branch> [<start point>]::
 
        This form switches branches by updating the index, working
        tree, and HEAD to reflect the specified branch.
@@ -31,6 +31,17 @@ were called and then checked out; in this case you can
 use the `--track` or `--no-track` options, which will be passed to
 'git branch'.  As a convenience, `--track` without `-b` implies branch
 creation; see the description of `--track` below.
++
+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+is reset. This is the transactional equivalent of
++
+------------
+$ git branch -f <branch> [<start point>]
+$ git checkout <branch>
+------------
++
+that is to say, the branch is not reset/created unless "git checkout" is
+successful.
 
 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
 
@@ -75,6 +86,12 @@ entries; instead, unmerged entries are ignored.
        Create a new branch named <new_branch> and start it at
        <start_point>; see linkgit:git-branch[1] for details.
 
+-B::
+       Creates the branch <new_branch> and start it at <start_point>;
+       if it already exists, then reset it to <start_point>. This is
+       equivalent to running "git branch" with "-f"; see
+       linkgit:git-branch[1] for details.
+
 -t::
 --track::
        When creating a new branch, set up "upstream" configuration. See
index a81cb6c28050e83778f11f2957d3489f50fa6004..60e38e6e275f5cf90a1ccba36dd3930cafa1b630 100644 (file)
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,12 @@ OPTIONS
        Be quiet, only report errors, but not the files that are
        successfully removed.
 
+-e <pattern>::
+--exclude=<pattern>::
+       Specify special exceptions to not be cleaned.  Each <pattern> is
+       the same form as in $GIT_DIR/info/excludes and this option can be
+       given multiple times.
+
 -x::
        Don't use the ignore rules.  This allows removing all untracked
        files, including build products.  This can be used (possibly in
index 77a0a2481a34f987aab4688002a6e6a0ae2e497f..966ba4f21337e72f7be0a5b852e76a8b12192f7f 100644 (file)
@@ -482,9 +482,11 @@ External data format::
        'M' SP <mode> SP <dataref> SP <path> LF
 ....
 +
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
 set by a prior `blob` command, or a full 40-byte SHA-1 of an
-existing Git blob object.
+existing Git blob object.  If `<mode>` is `040000`` then
+`<dataref>` must be the full 40-byte SHA-1 of an existing
+Git tree object or a mark reference set with `--import-marks`.
 
 Inline data format::
        The data content for the file has not been supplied yet.
@@ -509,6 +511,8 @@ in octal.  Git only supports the following modes:
 * `160000`: A gitlink, SHA-1 of the object refers to a commit in
   another repository. Git links can only be specified by SHA or through
   a commit mark. They are used to implement submodules.
+* `040000`: A subdirectory.  Subdirectories can only be specified by
+  SHA or through a tree mark set with `--import-marks`.
 
 In both formats `<path>` is the complete path of the file to be added
 (if not already existing) or modified (if already existing).
index 2c3c4d299472a265bfc4486816933ca3dc69b44f..e70cea932063b04f3ab3c78b882f4981514d663e 100644 (file)
@@ -49,15 +49,18 @@ OPTIONS
        linkgit:git-web--browse[1] for more information about this. If
        the script fails, the URL will be printed to stdout.
 
+start::
 --start::
        Start the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance.
 
+stop::
 --stop::
        Stop the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance,
        nor does it close the browser.
 
+restart::
 --restart::
        Restart the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance.
index bd919f2dfddc0165f7749e425a9b1d393558d46f..a7c8174d01810bfb28e331aafc27414dd6cfad50 100644 (file)
@@ -140,6 +140,12 @@ a space) at the start of each line:
        lines, show only a partial prefix.
        Non default number of digits can be specified with --abbrev=<n>.
 
+--debug::
+       After each line that describes a file, add more data about its
+       cache entry.  This is intended to show as much information as
+       possible for manual inspection; the exact format may change at
+       any time.
+
 \--::
        Do not interpret any more arguments as options.
 
index be23ad2359b486e4da2ef85962d73cdfed7097fe..3b87f1a1b67dbd64e9fd5e5cd11dd648e3f92ab2 100644 (file)
@@ -250,6 +250,13 @@ on top of the <upstream> branch using the given strategy, using
 the 'ours' strategy simply discards all patches from the <branch>,
 which makes little sense.
 
+-X <strategy-option>::
+--strategy-option=<strategy-option>::
+       Pass the <strategy-option> through to the merge strategy.
+       This implies `\--merge` and, if no strategy has been
+       specified, `-s recursive`.  Note the reversal of 'ours' and
+       'theirs' as noted in above for the `-m` option.
+
 -q::
 --quiet::
        Be quiet. Implies --no-stat.
@@ -459,6 +466,30 @@ sure that the current HEAD is "B", and call
 $ git rebase -i -p --onto Q O
 -----------------------------
 
+Reordering and editing commits usually creates untested intermediate
+steps.  You may want to check that your history editing did not break
+anything by running a test, or at least recompiling at intermediate
+points in history by using the "exec" command (shortcut "x").  You may
+do so by creating a todo list like this one:
+
+-------------------------------------------
+pick deadbee Implement feature XXX
+fixup f1a5c00 Fix to feature XXX
+exec make
+pick c0ffeee The oneline of the next commit
+edit deadbab The oneline of the commit after
+exec cd subdir; make test
+...
+-------------------------------------------
+
+The interactive rebase will stop when a command fails (i.e. exits with
+non-0 status) to give you an opportunity to fix the problem. You can
+continue with `git rebase --continue`.
+
+The "exec" command launches the command in a shell (the one specified
+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
+use shell features (like "cd", ">", ";" ...). The command is run from
+the root of the working tree.
 
 SPLITTING COMMITS
 -----------------
index 2fd054c1040ce43949382e4744a0d7a814a8d9bd..dae190a5f21d3835024ce263bf4bd9b08048cfa6 100644 (file)
@@ -55,7 +55,11 @@ specified.
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules when looking for changes. <when> can be
-       either "untracked", "dirty" or "all", which is the default. When
+       either "none", "untracked", "dirty" or "all", which is the default.
+       Using "none" will consider the submodule modified when it either contains
+       untracked or modified files or its HEAD differs from the commit recorded
+       in the superproject and can be used to override any settings of the
+       'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
        "untracked" is used submodules are not considered dirty when they only
        contain untracked content (but they are still scanned for modified
        content). Using "dirty" ignores all changes to the work tree of submodules,
index b09bd9761faa42f2bc87306ee5c1890647dce769..4b84d08fc87c13f6db371ecc52710bd09865d784 100644 (file)
@@ -646,6 +646,12 @@ svn.brokenSymlinkWorkaround::
        revision fetched.  If unset, 'git svn' assumes this option to
        be "true".
 
+svn.pathnameencoding::
+       This instructs git svn to recode pathnames to a given encoding.
+       It can be used by windows users and by those who work in non-utf8
+       locales to avoid corrupted file names with non-ASCII characters.
+       Valid encodings are the ones supported by Perl's Encode module.
+
 Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps
 options all affect the metadata generated and used by 'git svn'; they
 *must* be set in the configuration file before any history is imported
index 564586b943f439cb5ae04c4e76bc19d093c625da..2e2370ccdbc05a8926bae109a659fbcc9912e32f 100644 (file)
@@ -441,6 +441,8 @@ patterns are available:
 
 - `cpp` suitable for source code in the C and C++ languages.
 
+- `csharp` suitable for source code in the C# language.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
index 72a13d18e019ef00de0e8a79bbe5fe4651ab8dba..bcffd95ada574a8fb3c6e62e539e736a17170414 100644 (file)
@@ -44,6 +44,21 @@ submodule.<name>.update::
        This config option is overridden if 'git submodule update' is given
        the '--merge' or '--rebase' options.
 
+submodule.<name>.ignore::
+       Defines under what circumstances "git status" and the diff family show
+       a submodule as modified. When set to "all", it will never be considered
+       modified, "dirty" will ignore all changes to the submodules work tree and
+       takes only differences between the HEAD of the submodule and the commit
+       recorded in the superproject into account. "untracked" will additionally
+       let submodules with modified tracked files in their work tree show up.
+       Using "none" (the default when this option is not set) also shows
+       submodules that have untracked files in their work tree as changed.
+       If this option is also present in the submodules entry in .git/config of
+       the superproject, the setting there will override the one found in
+       .gitmodules.
+       Both settings can be overridden on the command line by using the
+       "--ignore-submodule" option.
+
 
 EXAMPLES
 --------
index 55b728632cb7fa18de0d1dc070f5007acef053e0..14af37c3f14854e87d9d28f60b5ff72eb189c37f 100644 (file)
@@ -42,6 +42,8 @@ information.
 
 * `data` can be anything the `fn` callback would want to use.
 
+* `show_all_errors` tells whether to stop at the first error or not.
+
 Initializing
 ------------
 
index 2c4f44bfb627f5b36264b9a8702ac9b2214cdf0b..f6d301a10f7b57edb623de8d5a78c90f4fbc71a7 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.2.2
+DEF_VER=v1.7.2.GIT
 
 LF='
 '
index 1f11618cfdd8d1f680bbdcb8672960fdd7074b9a..b4745a5412a206704ada573c7388e96b094ca050 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1873,7 +1873,7 @@ builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 
 config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
-http.s http.o: EXTRA_CPPFLAGS = -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
+http.s http.o: EXTRA_CPPFLAGS = -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
 http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
diff --git a/alias.c b/alias.c
index 372b7d809301f9e3e936459e405cd6f2627bd4a9..eb9f08b912b2089d434926fc6083ac5ab90c73bc 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -22,6 +22,13 @@ char *alias_lookup(const char *alias)
        return alias_val;
 }
 
+#define SPLIT_CMDLINE_BAD_ENDING 1
+#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+static const char *split_cmdline_errors[] = {
+       "cmdline ends with \\",
+       "unclosed quote"
+};
+
 int split_cmdline(char *cmdline, const char ***argv)
 {
        int src, dst, count = 0, size = 16;
@@ -53,7 +60,7 @@ int split_cmdline(char *cmdline, const char ***argv)
                                if (!c) {
                                        free(*argv);
                                        *argv = NULL;
-                                       return error("cmdline ends with \\");
+                                       return -SPLIT_CMDLINE_BAD_ENDING;
                                }
                        }
                        cmdline[dst++] = c;
@@ -66,7 +73,7 @@ int split_cmdline(char *cmdline, const char ***argv)
        if (quoted) {
                free(*argv);
                *argv = NULL;
-               return error("unclosed quote");
+               return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
        }
 
        ALLOC_GROW(*argv, count+1, size);
@@ -75,3 +82,6 @@ int split_cmdline(char *cmdline, const char ***argv)
        return count;
 }
 
+const char *split_cmdline_strerror(int split_cmdline_errno) {
+       return split_cmdline_errors[-split_cmdline_errno-1];
+}
index b556b11610480afd80cddd86a81af9737254ee36..060c042f8bcc2d402cb9908be3bdbd3bb180a862 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -141,7 +141,8 @@ static void show_list(const char *debug, int counted, int nr,
                enum object_type type;
                unsigned long size;
                char *buf = read_sha1_file(commit->object.sha1, &type, &size);
-               char *ep, *sp;
+               const char *subject_start;
+               int subject_len;
 
                fprintf(stderr, "%c%c%c ",
                        (flags & TREESAME) ? ' ' : 'T',
@@ -156,13 +157,9 @@ static void show_list(const char *debug, int counted, int nr,
                        fprintf(stderr, " %.*s", 8,
                                sha1_to_hex(pp->item->object.sha1));
 
-               sp = strstr(buf, "\n\n");
-               if (sp) {
-                       sp += 2;
-                       for (ep = sp; *ep && *ep != '\n'; ep++)
-                               ;
-                       fprintf(stderr, " %.*s", (int)(ep - sp), sp);
-               }
+               subject_len = find_commit_subject(buf, &subject_start);
+               if (subject_len)
+                       fprintf(stderr, " %.*s", subject_len, subject_start);
                fprintf(stderr, "\n");
        }
 }
index 2ab42aaf4da38b4ea45ef7f0a0f6b807313d4a22..93dc866f8c09a2da2308c4fd677178b043f2d4d5 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -159,7 +159,7 @@ void create_branch(const char *head,
                        dont_change_ref = 1;
                else if (!force)
                        die("A branch named '%s' already exists.", name);
-               else if (!is_bare_repository() && !strcmp(head, name))
+               else if (!is_bare_repository() && head && !strcmp(head, name))
                        die("Cannot force update the current branch.");
                forcing = 1;
        }
index 28e3be2eadadc71723da96334b8ff1dd22e73205..101535448f2b0405c280387b63f7ce82fb7dd350 100644 (file)
@@ -1407,7 +1407,8 @@ static void get_commit_info(struct commit *commit,
                            int detailed)
 {
        int len;
-       char *tmp, *endp, *reencoded, *message;
+       const char *subject;
+       char *reencoded, *message;
        static char author_name[1024];
        static char author_mail[1024];
        static char committer_name[1024];
@@ -1449,22 +1450,13 @@ static void get_commit_info(struct commit *commit,
                    &ret->committer_time, &ret->committer_tz);
 
        ret->summary = summary_buf;
-       tmp = strstr(message, "\n\n");
-       if (!tmp) {
-       error_out:
+       len = find_commit_subject(message, &subject);
+       if (len && len < sizeof(summary_buf)) {
+               memcpy(summary_buf, subject, len);
+               summary_buf[len] = 0;
+       } else {
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
-               free(reencoded);
-               return;
        }
-       tmp += 2;
-       endp = strchr(tmp, '\n');
-       if (!endp)
-               endp = tmp + strlen(tmp);
-       len = endp - tmp;
-       if (len >= sizeof(summary_buf) || len == 0)
-               goto error_out;
-       memcpy(summary_buf, tmp, len);
-       summary_buf[len] = 0;
        free(reencoded);
 }
 
index eef2b48d9856c90b2d8fd7b2e06f8179c08faeac..7250e5c23cee7deda557c16945373b59d99424ee 100644 (file)
@@ -32,7 +32,11 @@ struct checkout_opts {
        int writeout_stage;
        int writeout_error;
 
+       /* not set by parse_options */
+       int branch_exists;
+
        const char *new_branch;
+       const char *new_branch_force;
        const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
@@ -372,7 +376,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
 
-               topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+               set_porcelain_error_msgs(topts.msgs, "checkout");
 
                refresh_cache(REFRESH_QUIET);
 
@@ -391,6 +395,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
                topts.dir->exclude_per_dir = ".gitignore";
+               topts.show_all_errors = 1;
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           (unsigned char *)EMPTY_TREE_SHA1_BIN);
@@ -510,7 +515,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                        }
                }
                else
-                       create_branch(old->name, opts->new_branch, new->name, 0,
+                       create_branch(old->name, opts->new_branch, new->name,
+                                     opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
@@ -528,9 +534,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                        if (old->path && !strcmp(new->path, old->path))
                                fprintf(stderr, "Already on '%s'\n",
                                        new->name);
-                       else
+                       else if (opts->new_branch)
                                fprintf(stderr, "Switched to%s branch '%s'\n",
-                                       opts->new_branch ? " a new" : "",
+                                       opts->branch_exists ? " and reset" : " a new",
+                                       new->name);
+                       else
+                               fprintf(stderr, "Switched to branch '%s'\n",
                                        new->name);
                }
                if (old->path && old->name) {
@@ -656,7 +665,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        int dwim_new_local_branch = 1;
        struct option options[] = {
                OPT__QUIET(&opts.quiet),
-               OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+               OPT_STRING('b', NULL, &opts.new_branch, "branch",
+                          "create and checkout a new branch"),
+               OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+                          "create/reset and checkout a branch"),
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
                OPT_SET_INT('t', "track",  &opts.track, "track",
                        BRANCH_TRACK_EXPLICIT),
@@ -687,6 +699,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       /* we can assume from now on new_branch = !new_branch_force */
+       if (opts.new_branch && opts.new_branch_force)
+               die("-B cannot be used with -b");
+
+       /* copy -B over to -b, so that we can just check the latter */
+       if (opts.new_branch_force)
+               opts.new_branch = opts.new_branch_force;
+
        if (patch_mode && (opts.track > 0 || opts.new_branch
                           || opts.new_branch_log || opts.merge || opts.force))
                die ("--patch is incompatible with all other options");
@@ -708,7 +728,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
-                       die("--orphan and -b are mutually exclusive");
+                       die("--orphan and -b|-B are mutually exclusive");
                if (opts.track > 0)
                        die("--orphan cannot be used with -t");
                opts.new_branch = opts.new_orphan_branch;
@@ -857,8 +877,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                if (strbuf_check_branch_ref(&buf, opts.new_branch))
                        die("git checkout: we do not like '%s' as a branch name.",
                            opts.new_branch);
-               if (!get_sha1(buf.buf, rev))
-                       die("git checkout: branch %s already exists", opts.new_branch);
+               if (!get_sha1(buf.buf, rev)) {
+                       opts.branch_exists = 1;
+                       if (!opts.new_branch_force)
+                               die("git checkout: branch %s already exists",
+                                   opts.new_branch);
+               }
                strbuf_release(&buf);
        }
 
index fac64e6cd3717dfffb0f54124de9545f7cdc391a..b508d2cab482dffc97be1e8ee0481a6bb6157cf2 100644 (file)
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "string-list.h"
 #include "quote.h"
 
 static int force = -1; /* unset */
 
 static const char *const builtin_clean_usage[] = {
-       "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+       "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
        NULL
 };
 
@@ -26,6 +27,13 @@ static int git_clean_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+       struct string_list *exclude_list = opt->value;
+       string_list_append(exclude_list, arg);
+       return 0;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -36,6 +44,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        struct dir_struct dir;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
+       struct string_list exclude_list = { NULL, 0, 0, 0 };
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
@@ -44,6 +53,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN('f', "force", &force, "force"),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                "remove whole directories"),
+               { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+                 "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
                OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
                OPT_BOOLEAN('X', NULL, &ignored_only,
                                "remove only ignored files"),
@@ -81,6 +92,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (!ignored)
                setup_standard_excludes(&dir);
 
+       for (i = 0; i < exclude_list.nr; i++)
+               add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+
        pathspec = get_pathspec(prefix, argv);
 
        fill_directory(&dir, pathspec);
@@ -167,5 +181,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        free(seen);
 
        strbuf_release(&directory);
+       string_list_clear(&exclude_list, 0);
        return (errors != 0);
 }
index c4a577d5c5595a6c50ecaaba90690c286d30b7e2..66fdd2202495b2893c79b4f92b0fc2e715f5b7bb 100644 (file)
@@ -25,6 +25,7 @@
 #include "rerere.h"
 #include "unpack-trees.h"
 #include "quote.h"
+#include "submodule.h"
 
 static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@ -1073,6 +1074,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                status_format = STATUS_FORMAT_PORCELAIN;
 
        wt_status_prepare(&s);
+       gitmodules_config();
        git_config(git_status_config, &s);
        in_merge = file_exists(git_path("MERGE_HEAD"));
        argc = parse_options(argc, argv, prefix,
index f3d1660d023f7893373436617a9f729ce1d60f4d..79fee767b88a2d826311afb7a3299d67dedd3b94 100644 (file)
@@ -20,7 +20,7 @@ static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
 
-static int use_global_config, use_system_config;
+static int use_global_config, use_system_config, use_local_config;
 static const char *given_config_file;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
@@ -51,6 +51,7 @@ static struct option builtin_config_options[] = {
        OPT_GROUP("Config file location"),
        OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
        OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+       OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
        OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
        OPT_GROUP("Action"),
        OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
@@ -342,7 +343,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
-       if (use_global_config + use_system_config + !!given_config_file > 1) {
+       if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -358,6 +359,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
        }
        else if (use_system_config)
                config_exclusive_filename = git_etc_gitconfig();
+       else if (use_local_config)
+               config_exclusive_filename = git_pathdup("config");
        else if (given_config_file) {
                if (!is_absolute_path(given_config_file) && prefix)
                        config_exclusive_filename = prefix_filename(prefix,
index 5b64011de8222f06b5c772a6461278dea152919e..951c7c8994704543fc1784c87a17a1aa47ede257 100644 (file)
@@ -8,6 +8,7 @@
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static const char diff_files_usage[] =
 "git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
@@ -20,6 +21,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
        unsigned options = 0;
 
        init_revisions(&rev, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
 
index 04837494feba401c7f689eab5574768d3fd126de..2eb32bd9da8cfa4d7b1b99f9d22ffa5b3d6202e5 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static const char diff_cache_usage[] =
 "git diff-index [-m] [--cached] "
@@ -17,6 +18,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        int result;
 
        init_revisions(&rev, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
 
index 3c78bda5664e20086bcd500105b039d6241e3782..0d2a3e9fa2023cc673f1f6d9e17d3deeca1c7e9d 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static struct rev_info log_tree_opt;
 
@@ -112,6 +113,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        int read_stdin = 0;
 
        init_revisions(opt, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt->abbrev = 0;
        opt->diff = 1;
index 89ae89cde1e6d8a0a2e02209f5396263a81bda5b..a43d3263638daf083c52f37f3bd0872327d7459b 100644 (file)
@@ -13,6 +13,7 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "submodule.h"
 
 struct blobinfo {
        unsigned char sha1[20];
@@ -279,6 +280,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         */
 
        prefix = setup_git_directory_gently(&nongit);
+       gitmodules_config();
        git_config(git_diff_ui_config, NULL);
 
        if (diff_use_color_default == -1)
index 9fe25ff0b3dc0ae89c668b146b46a4ccbd457361..ab792d5db2ba6e96acd6f8401879442667b66cfb 100644 (file)
@@ -565,8 +565,8 @@ static void import_marks(char *input_file)
 int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
-       struct object_array commits = { 0, 0, NULL };
-       struct string_list extra_refs = { NULL, 0, 0, 0 };
+       struct object_array commits = OBJECT_ARRAY_INIT;
+       struct string_list extra_refs = STRING_LIST_INIT_NODUP;
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
        struct option options[] = {
index 1b67f5fda55896635e91852657259b47e722c889..ea14d5dc6b00b475b8817768b7d35d0bb54d5236 100644 (file)
@@ -544,40 +544,14 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
-struct tag_data {
-       struct ref **head;
-       struct ref ***tail;
-};
-
-static int add_to_tail(struct string_list_item *item, void *cb_data)
-{
-       struct tag_data *data = (struct tag_data *)cb_data;
-       struct ref *rm = NULL;
-
-       /* We have already decided to ignore this item */
-       if (!item->util)
-               return 0;
-
-       rm = alloc_ref(item->string);
-       rm->peer_ref = alloc_ref(item->string);
-       hashcpy(rm->old_sha1, item->util);
-
-       **data->tail = rm;
-       *data->tail = &rm->next;
-
-       return 0;
-}
-
 static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
-       struct string_list remote_refs = { NULL, 0, 0, 0 };
-       struct tag_data data;
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
-       data.head = head; data.tail = tail;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -631,10 +605,20 @@ static void find_non_local_tags(struct transport *transport,
                item->util = NULL;
 
        /*
-        * For all the tags in the remote_refs string list, call
-        * add_to_tail to add them to the list of refs to be fetched
+        * For all the tags in the remote_refs string list,
+        * add them to the list of refs to be fetched
         */
-       for_each_string_list(&remote_refs, add_to_tail, &data);
+       for_each_string_list_item(item, &remote_refs) {
+               /* Unless we have already decided to ignore this item... */
+               if (item->util)
+               {
+                       struct ref *rm = alloc_ref(item->string);
+                       rm->peer_ref = alloc_ref(item->string);
+                       hashcpy(rm->old_sha1, item->util);
+                       **tail = rm;
+                       *tail = &rm->next;
+               }
+       }
 
        string_list_clear(&remote_refs, 0);
 }
@@ -667,7 +651,7 @@ static int truncate_fetch_head(void)
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
@@ -891,7 +875,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
-       struct string_list list = { NULL, 0, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        struct remote *remote;
        int result = 0;
 
index bc3c5e6d3ec50eaa9829358ee0a82a1438c19f89..a76cd4e9d131eb76edb8a88f675dc14d4b3f597e 100644 (file)
@@ -38,8 +38,8 @@ void init_src_data(struct src_data *data)
        data->generic.strdup_strings = 1;
 }
 
-static struct string_list srcs = { NULL, 0, 0, 1 };
-static struct string_list origins = { NULL, 0, 0, 1 };
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
 
 static int handle_line(char *line)
 {
@@ -146,7 +146,7 @@ static void shortlog(const char *name, unsigned char *sha1,
        int i, count = 0;
        struct commit *commit;
        struct object *branch;
-       struct string_list subjects = { NULL, 0, 0, 1 };
+       struct string_list subjects = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
 
index 597f76bc42f51e0fc1906c287981e3d37dc0f536..b725ede0f7270637759b9fef52ab5450fc1712e2 100644 (file)
@@ -834,9 +834,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int external_grep_allowed__ignored;
        const char *show_in_pager = NULL, *default_pager = "dummy";
        struct grep_opt opt;
-       struct object_array list = { 0, 0, NULL };
+       struct object_array list = OBJECT_ARRAY_INIT;
        const char **paths = NULL;
-       struct string_list path_list = { NULL, 0, 0, 0 };
+       struct string_list path_list = STRING_LIST_INIT_NODUP;
        int i;
        int dummy;
        int nongit = 0, use_index = 1;
index 1b9b8a8b4ac2baff6b9676bcf23b148f147dac6a..bb4f612b3d48c453d551f251b65887beb283ec7b 100644 (file)
@@ -25,6 +25,7 @@ static int show_modified;
 static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
+static int debug_mode;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -162,35 +163,41 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       ce_stage(ce));
        }
        write_name(ce->name, ce_namelen(ce));
-}
-
-static int show_one_ru(struct string_list_item *item, void *cbdata)
-{
-       const char *path = item->string;
-       struct resolve_undo_info *ui = item->util;
-       int i, len;
-
-       len = strlen(path);
-       if (len < max_prefix_len)
-               return 0; /* outside of the prefix */
-       if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
-               return 0; /* uninterested */
-       for (i = 0; i < 3; i++) {
-               if (!ui->mode[i])
-                       continue;
-               printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
-                      find_unique_abbrev(ui->sha1[i], abbrev),
-                      i + 1);
-               write_name(path, len);
+       if (debug_mode) {
+               printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+               printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+               printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+               printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+               printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
        }
-       return 0;
 }
 
 static void show_ru_info(void)
 {
+       struct string_list_item *item;
+
        if (!the_index.resolve_undo)
                return;
-       for_each_string_list(the_index.resolve_undo, show_one_ru, NULL);
+
+       for_each_string_list_item(item, the_index.resolve_undo) {
+               const char *path = item->string;
+               struct resolve_undo_info *ui = item->util;
+               int i, len;
+
+               len = strlen(path);
+               if (len < max_prefix_len)
+                       continue; /* outside of the prefix */
+               if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+                       continue; /* uninterested */
+               for (i = 0; i < 3; i++) {
+                       if (!ui->mode[i])
+                               continue;
+                       printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+                              find_unique_abbrev(ui->sha1[i], abbrev),
+                              i + 1);
+                       write_name(path, len);
+               }
+       }
 }
 
 static void show_files(struct dir_struct *dir)
@@ -519,6 +526,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
                        "pretend that paths removed since <tree-ish> are still present"),
                OPT__ABBREV(&abbrev),
+               OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
                OPT_END()
        };
 
index e4560da19138cd67843f93a05bc6509d51cfe076..99654d02221c13ff98f268ad2dee99f16e794750 100644 (file)
@@ -137,7 +137,7 @@ static int split_maildir(const char *maildir, const char *dir,
        char name[PATH_MAX];
        int ret = -1;
        int i;
-       struct string_list list = {NULL, 0, 0, 1};
+       struct string_list list = STRING_LIST_INIT_DUP;
 
        if (populate_maildir_list(&list, maildir) < 0)
                goto out;
index 37ce4f589f6582abf0d1ef5bd263f590a16853d5..47e705ba9b6bb043b28183422d518b65cf513478 100644 (file)
@@ -486,7 +486,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
-                       die("Bad branch.%s.mergeoptions string", branch);
+                       die("Bad branch.%s.mergeoptions string: %s", branch,
+                           split_cmdline_strerror(argc));
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
@@ -704,7 +705,8 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
-       opts.msgs = get_porcelain_error_msgs();
+       opts.show_all_errors = 1;
+       set_porcelain_error_msgs(opts.msgs, "merge");
 
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
index 38574b89f7cfc127b2ea5044b3ebb5dabe726fb4..cdbb09473c0c45efff23a63e78b7623cad523350 100644 (file)
@@ -63,7 +63,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        const char **source, **destination, **dest_path;
        enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
        struct stat st;
-       struct string_list src_for_dst = {NULL, 0, 0, 0};
+       struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
 
        git_config(git_default_config, NULL);
 
index 06a38ac8c10126085e6477c205d450da089faae2..31f5c1c971381a9490b717e0413ee9a40260ef39 100644 (file)
@@ -220,7 +220,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
-       struct object_array revs = { 0, 0, NULL };
+       struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
index 69bc2f27ac414ce3532f6babcc68671b0b95d65a..e655eb7695faba13c4d9e8f25b9649ffec7195be 100644 (file)
@@ -22,13 +22,13 @@ static int progress;
 
 static const char **refspec;
 static int refspec_nr;
+static int refspec_alloc;
 
 static void add_refspec(const char *ref)
 {
-       int nr = refspec_nr + 1;
-       refspec = xrealloc(refspec, nr * sizeof(char *));
-       refspec[nr-1] = ref;
-       refspec_nr = nr;
+       refspec_nr++;
+       ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+       refspec[refspec_nr-1] = ref;
 }
 
 static void set_refspecs(const char **refs, int nr)
index d634b5a3d5212b8e49217d94430c294b911dfc7d..760817dbd7fec0086a0a1b62e58ab4438f227b7b 100644 (file)
@@ -530,7 +530,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
 static void check_aliased_updates(struct command *commands)
 {
        struct command *cmd;
-       struct string_list ref_list = { NULL, 0, 0, 0 };
+       struct string_list ref_list = STRING_LIST_INIT_NODUP;
 
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct string_list_item *item =
index 6699bc571235167f5cae60106522c09a10ae341d..48e0a6bf260cab18d44058ade554e3a9d5946b93 100644 (file)
@@ -134,7 +134,7 @@ static int add_branch(const char *key, const char *branchname,
 static int add(int argc, const char **argv)
 {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
-       struct string_list track = { NULL, 0, 0 };
+       struct string_list track = STRING_LIST_INIT_NODUP;
        const char *master = NULL;
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
@@ -596,7 +596,7 @@ static int mv(int argc, const char **argv)
        };
        struct remote *oldremote, *newremote;
        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
-       struct string_list remote_branches = { NULL, 0, 0, 0 };
+       struct string_list remote_branches = STRING_LIST_INIT_NODUP;
        struct rename_info rename;
        int i;
 
@@ -734,8 +734,8 @@ static int rm(int argc, const char **argv)
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT;
        struct known_remotes known_remotes = { NULL, NULL };
-       struct string_list branches = { NULL, 0, 0, 1 };
-       struct string_list skipped = { NULL, 0, 0, 1 };
+       struct string_list branches = STRING_LIST_INIT_DUP;
+       struct string_list skipped = STRING_LIST_INIT_DUP;
        struct branches_for_remote cb_data;
        int i, result;
 
@@ -1044,7 +1044,7 @@ static int show(int argc, const char **argv)
                OPT_END()
        };
        struct ref_states states;
-       struct string_list info_list = { NULL, 0, 0, 0 };
+       struct string_list info_list = STRING_LIST_INIT_NODUP;
        struct show_info info;
 
        argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
@@ -1483,7 +1483,7 @@ static int get_one_entry(struct remote *remote, void *priv)
 
 static int show_all(void)
 {
-       struct string_list list = { NULL, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        int result;
 
        list.strdup_strings = 1;
index 39ad60169d6f4cd53e560e922410b48cc01f7886..9b1e3a7cf50674ffdd834d08edba184f5c7c285d 100644 (file)
@@ -40,7 +40,7 @@ static int git_rerere_gc_config(const char *var, const char *value, void *cb)
 
 static void garbage_collect(struct string_list *rr)
 {
-       struct string_list to_remove = { NULL, 0, 0, 1 };
+       struct string_list to_remove = STRING_LIST_INIT_DUP;
        DIR *dir;
        struct dirent *e;
        int i, cutoff;
@@ -102,7 +102,7 @@ static int diff_two(const char *file1, const char *label1,
 
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int i, fd, flags = 0;
 
        if (2 < argc) {
index 8b9d829a73a0862dfdf2c2499b18878489620f16..9215e665046a8991b57f8d4cfda123dae084414c 100644 (file)
@@ -102,9 +102,9 @@ struct commit_message {
 static int get_message(const char *raw_message, struct commit_message *out)
 {
        const char *encoding;
-       const char *p, *abbrev, *eol;
+       const char *abbrev, *subject;
+       int abbrev_len, subject_len;
        char *q;
-       int abbrev_len, oneline_len;
 
        if (!raw_message)
                return -1;
@@ -125,27 +125,17 @@ static int get_message(const char *raw_message, struct commit_message *out)
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
-       /* Find beginning and end of commit subject. */
-       p = out->message;
-       while (*p && (*p != '\n' || p[1] != '\n'))
-               p++;
-       if (*p) {
-               p += 2;
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-       } else
-               eol = p;
-       oneline_len = eol - p;
+       subject_len = find_commit_subject(out->message, &subject);
 
        out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
-                             strlen("... ") + oneline_len + 1);
+                             strlen("... ") + subject_len + 1);
        q = out->parent_label;
        q = mempcpy(q, "parent of ", strlen("parent of "));
        out->label = q;
        q = mempcpy(q, abbrev, abbrev_len);
        q = mempcpy(q, "... ", strlen("... "));
        out->subject = q;
-       q = mempcpy(q, p, oneline_len);
+       q = mempcpy(q, subject, subject_len);
        *q = '\0';
        return 0;
 }
index 0b2a9ad1a9a8a825c2e2104f8a9f729a9cb03d1a..be9b512eebc72984e6ff4cdd4859ab7951539e19 100644 (file)
@@ -120,7 +120,7 @@ static int add_existing(const char *refname, const unsigned char *sha1, int flag
  */
 static int exclude_existing(const char *match)
 {
-       static struct string_list existing_refs = { NULL, 0, 0, 0 };
+       static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
        char buf[1024];
        int matchlen = match ? strlen(match) : 0;
 
diff --git a/cache.h b/cache.h
index 1e690d1240fb0fb752c0f0660c4c2dff2102e37f..eb77e1df48814b92e76436ca3184319147192f7d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -179,8 +179,7 @@ struct cache_entry {
 #define CE_UNHASHED  (0x200000)
 #define CE_CONFLICTED (0x800000)
 
-/* Only remove in work directory, not index */
-#define CE_WT_REMOVE (0x400000)
+#define CE_WT_REMOVE (0x400000) /* remove in work directory */
 
 #define CE_UNPACKED  (0x1000000)
 
@@ -641,6 +640,9 @@ extern char *git_pathdup(const char *fmt, ...)
 /* Return a statically allocated filename matching the sha1 signature */
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *git_path_submodule(const char *path, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
 extern char *sha1_file_name(const unsigned char *sha1);
 extern char *sha1_pack_name(const unsigned char *sha1);
 extern char *sha1_pack_index_name(const unsigned char *sha1);
@@ -811,6 +813,7 @@ const char *show_date_relative(unsigned long time, int tz,
                               char *timebuf,
                               size_t timebuf_size);
 int parse_date(const char *date, char *buf, int bufsize);
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
 void datestamp(char *buf, int bufsize);
 #define approxidate(s) approxidate_careful((s), NULL)
 unsigned long approxidate_careful(const char *, int *);
@@ -1096,6 +1099,8 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
+/* Takes a negative value returned by split_cmdline */
+const char *split_cmdline_strerror(int cmdline_errno);
 
 /* builtin/merge.c */
 int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
index e9b07509678f5a4f61e5bedadea14b726e290ed1..0094ec1c9278332f736d2814c847b272788a7dd3 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -315,6 +315,25 @@ int parse_commit(struct commit *item)
        return ret;
 }
 
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+       const char *eol;
+       const char *p = commit_buffer;
+
+       while (*p && (*p != '\n' || p[1] != '\n'))
+               p++;
+       if (*p) {
+               p += 2;
+               for (eol = p; *eol && *eol != '\n'; eol++)
+                       ; /* do nothing */
+       } else
+               eol = p;
+
+       *subject = p;
+
+       return eol - p;
+}
+
 struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 {
        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
index eb2b8ac3cd5f375e70354e8c364abd036b0966ed..9113bbe4889d71e824348edcb920110598db18d2 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -41,6 +41,9 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
 
 int parse_commit(struct commit *item);
 
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
 struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
 unsigned commit_list_count(const struct commit_list *l);
 struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
index 09c524105cfa5fca3a5a860bbbed35f5eff84f79..00850864371e16d1fa572b0d0e5832b8a88d8666 100755 (executable)
 #     "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
 #   Be careful if "..." contains things that will be expanded by shell "eval"
 #   or printf.
+# hooks.emailmaxlines
+#   The maximum number of lines that should be included in the generated
+#   email body. If not specified, there is no limit.
+#   Lines beyond the limit are suppressed and counted, and a final
+#   line is added indicating the number of suppressed lines.
 #
 # Notes
 # -----
@@ -84,6 +89,7 @@ generate_email()
        oldrev=$(git rev-parse $1)
        newrev=$(git rev-parse $2)
        refname="$3"
+       maxlines=$4
 
        # --- Interpret
        # 0000->1234 (create)
@@ -192,7 +198,12 @@ generate_email()
                fn_name=atag
                ;;
        esac
-       generate_${change_type}_${fn_name}_email
+
+       if [ -z "$maxlines" ]; then
+               generate_${change_type}_${fn_name}_email
+       else
+               generate_${change_type}_${fn_name}_email | limit_lines $maxlines
+       fi
 
        generate_email_footer
 }
@@ -642,6 +653,24 @@ show_new_revisions()
 }
 
 
+limit_lines()
+{
+       lines=0
+       skipped=0
+       while IFS="" read -r line; do
+               lines=$((lines + 1))
+               if [ $lines -gt $1 ]; then
+                       skipped=$((skipped + 1))
+               else
+                       printf "%s\n" "$line"
+               fi
+       done
+       if [ $skipped -ne 0 ]; then
+               echo "... $skipped lines suppressed ..."
+       fi
+}
+
+
 send_mail()
 {
        if [ -n "$envelopesender" ]; then
@@ -679,6 +708,7 @@ announcerecipients=$(git config hooks.announcelist)
 envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
 custom_showrev=$(git config hooks.showrev)
+maxlines=$(git config hooks.emailmaxlines)
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
@@ -691,6 +721,6 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
 else
        while read oldrev newrev refname
        do
-               generate_email $oldrev $newrev $refname | send_mail
+               generate_email $oldrev $newrev $refname $maxlines | send_mail
        done
 fi
diff --git a/date.c b/date.c
index 3c981f7eb5ebaed1ba621f3d4cd960f14145b97a..00f9eb5d0b9730107a8e08e92eb04d4cc9233595 100644 (file)
--- a/date.c
+++ b/date.c
@@ -586,7 +586,7 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
 
 /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
    (i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
 {
        struct tm tm;
        int tm_gmt;
@@ -642,17 +642,16 @@ int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
 
        if (!tm_gmt)
                *timestamp -= *offset * 60;
-       return 1; /* success */
+       return 0; /* success */
 }
 
 int parse_date(const char *date, char *result, int maxlen)
 {
        unsigned long timestamp;
        int offset;
-       if (parse_date_toffset(date, &timestamp, &offset) > 0)
-               return date_string(timestamp, offset, result, maxlen);
-       else
+       if (parse_date_basic(date, &timestamp, &offset))
                return -1;
+       return date_string(timestamp, offset, result, maxlen);
 }
 
 enum date_mode parse_date_format(const char *format)
@@ -1004,9 +1003,8 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv)
        int offset;
        int errors = 0;
 
-       if (parse_date_toffset(date, &timestamp, &offset) > 0)
+       if (!parse_date_basic(date, &timestamp, &offset))
                return timestamp;
-
        return approxidate_str(date, tv, &errors);
 }
 
@@ -1019,7 +1017,7 @@ unsigned long approxidate_careful(const char *date, int *error_ret)
        if (!error_ret)
                error_ret = &dummy;
 
-       if (parse_date_toffset(date, &timestamp, &offset) > 0) {
+       if (!parse_date_basic(date, &timestamp, &offset)) {
                *error_ret = 0;
                return timestamp;
        }
index 8b8978ae6d1b4d947952b7fe9ec9cea013aaa8c3..392ce2bef05746cea7922d39da67bf25d1d3d192 100644 (file)
@@ -68,11 +68,16 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
                                      unsigned ce_option, unsigned *dirty_submodule)
 {
        int changed = ce_match_stat(ce, st, ce_option);
-       if (S_ISGITLINK(ce->ce_mode)
-           && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
-           && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
-           && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
-               *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+       if (S_ISGITLINK(ce->ce_mode)) {
+               unsigned orig_flags = diffopt->flags;
+               if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+                       set_diffopt_flags_from_submodule_config(diffopt, ce->name);
+               if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+                       changed = 0;
+               else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
+                   && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
+                       *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+               diffopt->flags = orig_flags;
        }
        return changed;
 }
index 43aeeba2e0fd9c3c175dbc74a6f488e2c352c928..ce9e783407437bb1e0efc6d5bc2392af26da5a41 100644 (file)
@@ -64,7 +64,8 @@ static int queue_diff(struct diff_options *o,
 
        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
                char buffer1[PATH_MAX], buffer2[PATH_MAX];
-               struct string_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
+               struct string_list p1 = STRING_LIST_INIT_DUP;
+               struct string_list p2 = STRING_LIST_INIT_DUP;
                int len1 = 0, len2 = 0, i1, i2, ret = 0;
 
                if (name1 && read_directory(name1, &p1))
diff --git a/diff.c b/diff.c
index 93004922dee5b4f148dc8e6be5be469f1ffe75e6..6fb97d4623192167e9b9f83b02dfe9250f028c0a 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
+static struct diff_options default_diff_options;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -107,6 +108,9 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
 
+       if (!strcmp(var, "diff.ignoresubmodules"))
+               handle_ignore_submodules_arg(&default_diff_options, value);
+
        return git_diff_basic_config(var, value, cb);
 }
 
@@ -141,6 +145,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!prefixcmp(var, "submodule."))
+               return parse_submodule_config_option(var, value);
+
        return git_color_default_config(var, value, cb);
 }
 
@@ -2819,7 +2826,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
 
 void diff_setup(struct diff_options *options)
 {
-       memset(options, 0, sizeof(*options));
+       memcpy(options, &default_diff_options, sizeof(*options));
 
        options->file = stdout;
 
@@ -2995,9 +3002,100 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va
 
 static int diff_scoreopt_parse(const char *opt);
 
+static inline int short_opt(char opt, const char **argv,
+                           const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != opt)
+               return 0;
+       if (arg[2] != '\0') {
+               *optarg = arg + 2;
+               return 1;
+       }
+       if (!argv[1])
+               die("Option '%c' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+int parse_long_opt(const char *opt, const char **argv,
+                  const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != '-')
+               return 0;
+       arg += strlen("--");
+       if (prefixcmp(arg, opt))
+               return 0;
+       arg += strlen(opt);
+       if (*arg == '=') { /* sticked form: --option=value */
+               *optarg = arg + 1;
+               return 1;
+       }
+       if (*arg != '\0')
+               return 0;
+       /* separate form: --option value */
+       if (!argv[1])
+               die("Option '--%s' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+static int stat_opt(struct diff_options *options, const char **av)
+{
+       const char *arg = av[0];
+       char *end;
+       int width = options->stat_width;
+       int name_width = options->stat_name_width;
+       int argcount = 1;
+
+       arg += strlen("--stat");
+       end = (char *)arg;
+
+       switch (*arg) {
+       case '-':
+               if (!prefixcmp(arg, "-width")) {
+                       arg += strlen("-width");
+                       if (*arg == '=')
+                               width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-width' requires a value");
+                       else if (!*arg) {
+                               width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               } else if (!prefixcmp(arg, "-name-width")) {
+                       arg += strlen("-name-width");
+                       if (*arg == '=')
+                               name_width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-name-width' requires a value");
+                       else if (!*arg) {
+                               name_width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               }
+               break;
+       case '=':
+               width = strtoul(arg+1, &end, 10);
+               if (*end == ',')
+                       name_width = strtoul(end+1, &end, 10);
+       }
+
+       /* Important! This checks all the error cases! */
+       if (*end)
+               return 0;
+       options->output_format |= DIFF_FORMAT_DIFFSTAT;
+       options->stat_name_width = name_width;
+       options->stat_width = width;
+       return argcount;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
        const char *arg = av[0];
+       const char *optarg;
+       int argcount;
 
        /* Output format options */
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
@@ -3034,33 +3132,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat")) {
-               char *end;
-               int width = options->stat_width;
-               int name_width = options->stat_name_width;
-               arg += 6;
-               end = (char *)arg;
-
-               switch (*arg) {
-               case '-':
-                       if (!prefixcmp(arg, "-width="))
-                               width = strtoul(arg + 7, &end, 10);
-                       else if (!prefixcmp(arg, "-name-width="))
-                               name_width = strtoul(arg + 12, &end, 10);
-                       break;
-               case '=':
-                       width = strtoul(arg+1, &end, 10);
-                       if (*end == ',')
-                               name_width = strtoul(end+1, &end, 10);
-               }
-
-               /* Important! This checks all the error cases! */
-               if (*end)
-                       return 0;
-               options->output_format |= DIFF_FORMAT_DIFFSTAT;
-               options->stat_name_width = name_width;
-               options->stat_width = width;
-       }
+       else if (!prefixcmp(arg, "--stat"))
+               /* --stat, --stat-width, or --stat-name-width */
+               return stat_opt(options, av);
 
        /* renames options */
        else if (!prefixcmp(arg, "-B")) {
@@ -3154,10 +3228,11 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else
                        die("bad --word-diff argument: %s", type);
        }
-       else if (!prefixcmp(arg, "--word-diff-regex=")) {
+       else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
-               options->word_regex = arg + 18;
+               options->word_regex = optarg;
+               return argcount;
        }
        else if (!strcmp(arg, "--exit-code"))
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
@@ -3171,11 +3246,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_SET(options, ALLOW_TEXTCONV);
        else if (!strcmp(arg, "--no-textconv"))
                DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
-       else if (!strcmp(arg, "--ignore-submodules"))
+       else if (!strcmp(arg, "--ignore-submodules")) {
+               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       else if (!prefixcmp(arg, "--ignore-submodules="))
+       } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg + 20);
-       else if (!strcmp(arg, "--submodule"))
+       else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
        else if (!prefixcmp(arg, "--submodule=")) {
                if (!strcmp(arg + 12, "log"))
@@ -3185,18 +3262,26 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        /* misc options */
        else if (!strcmp(arg, "-z"))
                options->line_termination = 0;
-       else if (!prefixcmp(arg, "-l"))
-               options->rename_limit = strtoul(arg+2, NULL, 10);
-       else if (!prefixcmp(arg, "-S"))
-               options->pickaxe = arg + 2;
+       else if ((argcount = short_opt('l', av, &optarg))) {
+               options->rename_limit = strtoul(optarg, NULL, 10);
+               return argcount;
+       }
+       else if ((argcount = short_opt('S', av, &optarg))) {
+               options->pickaxe = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--pickaxe-all"))
                options->pickaxe_opts = DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-       else if (!prefixcmp(arg, "-O"))
-               options->orderfile = arg + 2;
-       else if (!prefixcmp(arg, "--diff-filter="))
-               options->filter = arg + 14;
+       else if ((argcount = short_opt('O', av, &optarg))) {
+               options->orderfile = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
+               options->filter = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
        else if (!prefixcmp(arg, "--abbrev=")) {
@@ -3206,20 +3291,25 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else if (40 < options->abbrev)
                        options->abbrev = 40;
        }
-       else if (!prefixcmp(arg, "--src-prefix="))
-               options->a_prefix = arg + 13;
-       else if (!prefixcmp(arg, "--dst-prefix="))
-               options->b_prefix = arg + 13;
+       else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
+               options->a_prefix = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
+               options->b_prefix = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--no-prefix"))
                options->a_prefix = options->b_prefix = "";
        else if (opt_arg(arg, '\0', "inter-hunk-context",
                         &options->interhunkcontext))
                ;
-       else if (!prefixcmp(arg, "--output=")) {
-               options->file = fopen(arg + strlen("--output="), "w");
+       else if ((argcount = parse_long_opt("output", av, &optarg))) {
+               options->file = fopen(optarg, "w");
                if (!options->file)
                        die_errno("Could not open '%s'", arg + strlen("--output="));
                options->close_file = 1;
+               return argcount;
        } else
                return 0;
        return 1;
@@ -4107,6 +4197,24 @@ int diff_result_code(struct diff_options *opt, int status)
        return result;
 }
 
+/*
+ * Shall changes to this submodule be ignored?
+ *
+ * Submodule changes can be configured to be ignored separately for each path,
+ * but that configuration can be overridden from the command line.
+ */
+static int is_submodule_ignored(const char *path, struct diff_options *options)
+{
+       int ignored = 0;
+       unsigned orig_flags = options->flags;
+       if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+               set_diffopt_flags_from_submodule_config(options, path);
+       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+               ignored = 1;
+       options->flags = orig_flags;
+       return ignored;
+}
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -4114,7 +4222,7 @@ void diff_addremove(struct diff_options *options,
 {
        struct diff_filespec *one, *two;
 
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+       if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
                return;
 
        /* This may look odd, but it is a preparation for
@@ -4161,8 +4269,8 @@ void diff_change(struct diff_options *options,
 {
        struct diff_filespec *one, *two;
 
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
-                       && S_ISGITLINK(new_mode))
+       if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
+           is_submodule_ignored(concatpath, options))
                return;
 
        if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
diff --git a/diff.h b/diff.h
index 6fff024e3ad9462c717595cd90d7fcca1da6410c..bf2f44d840735684c89f654ee6f3b3a935af2c55 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -77,6 +77,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
 #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
 #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
+#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
 
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
@@ -217,6 +218,13 @@ extern void diff_unmerge(struct diff_options *,
 #define DIFF_SETUP_USE_CACHE           2
 #define DIFF_SETUP_USE_SIZE_CACHE      4
 
+/*
+ * Poor man's alternative to parse-option, to allow both sticked form
+ * (--option=value) and separate form (--option value).
+ */
+extern int parse_long_opt(const char *opt, const char **argv,
+                        const char **optarg);
+
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
index ddad289dae37c2115f57f88dc207f0d2fa6ab1f3..dd51ac48b60d93031aa761d6731dd066c04e6989 100644 (file)
@@ -2131,6 +2131,7 @@ static void file_change_m(struct branch *b)
        case S_IFREG | 0644:
        case S_IFREG | 0755:
        case S_IFLNK:
+       case S_IFDIR:
        case S_IFGITLINK:
                /* ok */
                break;
@@ -2176,23 +2177,28 @@ static void file_change_m(struct branch *b)
                 * another repository.
                 */
        } else if (inline_data) {
+               if (S_ISDIR(mode))
+                       die("Directories cannot be specified 'inline': %s",
+                               command_buf.buf);
                if (p != uq.buf) {
                        strbuf_addstr(&uq, p);
                        p = uq.buf;
                }
                read_next_command();
                parse_and_store_blob(&last_blob, sha1, 0);
-       } else if (oe) {
-               if (oe->type != OBJ_BLOB)
-                       die("Not a blob (actually a %s): %s",
-                               typename(oe->type), command_buf.buf);
        } else {
-               enum object_type type = sha1_object_info(sha1, NULL);
+               enum object_type expected = S_ISDIR(mode) ?
+                                               OBJ_TREE: OBJ_BLOB;
+               enum object_type type = oe ? oe->type :
+                                       sha1_object_info(sha1, NULL);
                if (type < 0)
-                       die("Blob not found: %s", command_buf.buf);
-               if (type != OBJ_BLOB)
-                       die("Not a blob (actually a %s): %s",
-                           typename(type), command_buf.buf);
+                       die("%s not found: %s",
+                                       S_ISDIR(mode) ?  "Tree" : "Blob",
+                                       command_buf.buf);
+               if (type != expected)
+                       die("Not a %s (actually a %s): %s",
+                               typename(expected), typename(type),
+                               command_buf.buf);
        }
 
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
index b7342e22c88993756e7beb6582a896087e52a034..e6f6ecda177c66a79b18359f7bf8cad9c4744c8c 100755 (executable)
@@ -57,6 +57,13 @@ resolve_full_httpd () {
                httpd_only="${httpd%% *}" # cut on first space
                return
                ;;
+       *webrick*)
+               # server is started by running via generated webrick.rb in
+               # $fqgitdir/gitweb
+               full_httpd="$fqgitdir/gitweb/webrick.rb"
+               httpd_only="${httpd%% *}" # cut on first space
+               return
+               ;;
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -188,40 +195,53 @@ GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
 export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 
 webrick_conf () {
+       # webrick seems to have no way of passing arbitrary environment
+       # variables to the underlying CGI executable, so we wrap the
+       # actual gitweb.cgi using a shell script to force it
+  wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
+       cat > "$wrapper" <<EOF
+#!/bin/sh
+# we use this shell script wrapper around the real gitweb.cgi since
+# there appears to be no other way to pass arbitrary environment variables
+# into the CGI process
+GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
+exec $root/gitweb.cgi
+EOF
+       chmod +x "$wrapper"
+
+       # This assumes _ruby_ is in the user's $PATH. that's _one_
+       # portable way to run ruby, which could be installed anywhere, really.
        # generate a standalone server script in $fqgitdir/gitweb.
        cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
+#!/usr/bin/env ruby
 require 'webrick'
-require 'yaml'
-options = YAML::load_file(ARGV[0])
-options[:StartCallback] = proc do
-  File.open(options[:PidFile],"w") do |f|
-    f.puts Process.pid
-  end
-end
-options[:ServerType] = WEBrick::Daemon
+require 'logger'
+options = {
+  :Port => $port,
+  :DocumentRoot => "$root",
+  :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
+  :AccessLog => [
+    [ Logger.new('$fqgitdir/gitweb/access.log'),
+      WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
+  ],
+  :DirectoryIndex => ["gitweb.cgi"],
+  :CGIInterpreter => "$wrapper",
+  :StartCallback => lambda do
+    File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
+  end,
+  :ServerType => WEBrick::Daemon,
+}
+options[:BindAddress] = '127.0.0.1' if "$local" == "true"
 server = WEBrick::HTTPServer.new(options)
 ['INT', 'TERM'].each do |signal|
   trap(signal) {server.shutdown}
 end
 server.start
 EOF
-       # generate a shell script to invoke the above ruby script,
-       # which assumes _ruby_ is in the user's $PATH. that's _one_
-       # portable way to run ruby, which could be installed anywhere,
-       # really.
-       cat >"$fqgitdir/gitweb/$httpd" <<EOF
-#!/bin/sh
-exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
-EOF
-       chmod +x "$fqgitdir/gitweb/$httpd"
-
-       cat >"$conf" <<EOF
-:Port: $port
-:DocumentRoot: "$root"
-:DirectoryIndex: ["gitweb.cgi"]
-:PidFile: "$fqgitdir/pid"
-EOF
-       test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+       chmod +x "$fqgitdir/gitweb/$httpd.rb"
+       # configuration is embedded in server script file, webrick.rb
+       rm -f "$conf"
 }
 
 lighttpd_conf () {
index b52a7410bcb7b37dce0f4d6213dddedd2c1e42e7..bd7ab02f1161d2d97ea57b2a2ceacc307f6a74e8 100755 (executable)
@@ -264,17 +264,35 @@ merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo fa
 
 last_status=0
 rollup_status=0
+rerere=false
+
+files_to_merge() {
+    if test "$rerere" = true
+    then
+       git rerere status
+    else
+       git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u
+    fi
+}
+
 
 if test $# -eq 0 ; then
-    files=$(git ls-files -u | sed -e 's/^[^    ]*      //' | sort -u)
+    cd_to_toplevel
+
+    if test -e "$GIT_DIR/MERGE_RR"
+    then
+       rerere=true
+    fi
+
+    files=$(files_to_merge)
     if test -z "$files" ; then
        echo "No files need merging"
        exit 0
     fi
-    echo Merging the files: "$files"
-    git ls-files -u |
-    sed -e 's/^[^      ]*      //' |
-    sort -u |
+    printf "Merging:\n"
+    printf "$files\n"
+
+    files_to_merge |
     while IFS= read i
     do
        if test $last_status -ne 0; then
index a09a44ec4ce3d82cebc5f972f09e94c7c35dc948..8eb74d45debe7ab2bd2cc9b5ed57b1ba49d94bdb 100755 (executable)
@@ -273,6 +273,15 @@ then
        exit
 fi
 
+if test true = "$rebase"
+then
+       o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
+       if test "$oldremoteref" = "$o"
+       then
+               unset oldremoteref
+       fi
+fi
+
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
index b94c2a03867ddcd1fa0c2051f81cf14547b550f0..3419247d03e4147c777a1c7cbd7a39a61c1129df 100755 (executable)
@@ -537,6 +537,34 @@ do_next () {
                esac
                record_in_rewritten $sha1
                ;;
+       x|"exec")
+               read -r command rest < "$TODO"
+               mark_action_done
+               printf 'Executing: %s\n' "$rest"
+               # "exec" command doesn't take a sha1 in the todo-list.
+               # => can't just use $sha1 here.
+               git rev-parse --verify HEAD > "$DOTEST"/stopped-sha
+               ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+               status=$?
+               if test "$status" -ne 0
+               then
+                       warn "Execution failed: $rest"
+                       warn "You can fix the problem, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit "$status"
+               fi
+               # Run in subshell because require_clean_work_tree can die.
+               if ! (require_clean_work_tree)
+               then
+                       warn "Commit or stash your changes, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit 1
+               fi
+               ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
                if git rev-parse --verify -q "$sha1" >/dev/null
@@ -591,22 +619,30 @@ do_rest () {
 # skip picking commits whose parents are unchanged
 skip_unnecessary_picks () {
        fd=3
-       while read -r command sha1 rest
+       while read -r command rest
        do
                # fd=3 means we skip the command
-               case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
-               3,pick,"$ONTO"*|3,p,"$ONTO"*)
+               case "$fd,$command" in
+               3,pick|3,p)
                        # pick a commit whose parent is current $ONTO -> skip
-                       ONTO=$sha1
+                       sha1=$(printf '%s' "$rest" | cut -d ' ' -f 1)
+                       case "$(git rev-parse --verify --quiet "$sha1"^)" in
+                       "$ONTO"*)
+                               ONTO=$sha1
+                               ;;
+                       *)
+                               fd=1
+                               ;;
+                       esac
                        ;;
-               3,#*|3,,*)
+               3,#*|3,)
                        # copy comments
                        ;;
                *)
                        fd=1
                        ;;
                esac
-               printf '%s\n' "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+               printf '%s\n' "$command${rest:+ }$rest" >&$fd
        done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
        mv -f "$TODO".new "$TODO" &&
        case "$(peek_next_command)" in
@@ -957,6 +993,7 @@ first and then run 'git rebase --continue' again."
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
 #  f, fixup = like "squash", but discard this commit's log message
+#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
index 1b9ea48cd713d7f6b25e529d09743447635f6432..7508463b30e7d1659ac9423a19d195e494600bd3 100755 (executable)
@@ -44,6 +44,7 @@ To restore the original branch and stop rebasing run \"git rebase --abort\".
 "
 unset newbase
 strategy=recursive
+strategy_opts=
 do_merge=
 dotest="$GIT_DIR"/rebase-merge
 prec=4
@@ -112,7 +113,7 @@ call_merge () {
        then
                export GIT_MERGE_VERBOSITY=1
        fi
-       git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
+       eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
        rv=$?
        case "$rv" in
        0)
@@ -294,6 +295,27 @@ do
        -M|-m|--m|--me|--mer|--merg|--merge)
                do_merge=t
                ;;
+       -X*|--strategy-option*)
+               case "$#,$1" in
+               1,-X|1,--strategy-option)
+                       usage ;;
+               *,-X|*,--strategy-option)
+                       newopt="$2"
+                       shift ;;
+               *,--strategy-option=*)
+                       newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;;
+               *,-X*)
+                       newopt="$(expr " $1" : ' -X\(.*\)')" ;;
+               1,*)
+                       usage ;;
+               esac
+               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
+               do_merge=t
+               if test -n "$strategy"
+               then
+                       strategy=recursive
+               fi
+               ;;
        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
                --strateg=*|--strategy=*|\
        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
index c4163584a93ba594141aa8af99948fc8929605d7..9b046b693fe82c992b3844bf6e8f79214dfa7a59 100755 (executable)
@@ -494,6 +494,7 @@ sub cmd_set_tree {
 
 sub cmd_dcommit {
        my $head = shift;
+       command_noisy(qw/update-index --refresh/);
        git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
                'Cannot dcommit with a dirty index.  Commit your changes first, '
                . "or stash them with `git stash'.\n";
@@ -1819,6 +1820,7 @@ sub read_all_remotes {
                        die("svn-remote.$remote: remote ref '$remote_ref' "
                            . "must start with 'refs/'\n")
                                unless $remote_ref =~ m{^refs/};
+                       $local_ref = uri_decode($local_ref);
                        $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
                        $r->{$remote}->{svm} = {} if $use_svm_props;
                } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
@@ -1831,6 +1833,7 @@ sub read_all_remotes {
                        die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
                            . "must start with 'refs/'\n")
                                unless $remote_ref =~ m{^refs/};
+                       $local_ref = uri_decode($local_ref);
                        my $rs = {
                            t => $t,
                            remote => $remote,
@@ -2956,18 +2959,29 @@ sub other_gs {
        my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
        unless ($gs) {
                my $ref_id = $old_ref_id;
-               $ref_id =~ s/\@\d+$//;
+               $ref_id =~ s/\@\d+-*$//;
                $ref_id .= "\@$r";
                # 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" unless $::_q > 1;
                my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
                if ($u =~ s#^\Q$url\E(/|$)##) {
                        $p = $u;
                        $u = $url;
                        $repo_id = $self->{repo_id};
                }
-               $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+               while (1) {
+                       # It is possible to tag two different subdirectories at
+                       # the same revision.  If the url for an existing ref
+                       # does not match, we must either find a ref with a
+                       # matching url or create a new ref by growing a tail.
+                       $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+                       my (undef, $max_commit) = $gs->rev_map_max(1);
+                       last if (!$max_commit);
+                       my ($url) = ::cmt_metadata($max_commit);
+                       last if ($url eq $gs->full_url);
+                       $ref_id .= '-';
+               }
+               print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
        }
        $gs
 }
@@ -4050,6 +4064,7 @@ sub new {
        $self->{absent_dir} = {};
        $self->{absent_file} = {};
        $self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
+       $self->{pathnameencoding} = Git::config('svn.pathnameencoding');
        $self;
 }
 
@@ -4133,6 +4148,10 @@ sub open_directory {
 
 sub git_path {
        my ($self, $path) = @_;
+       if (my $enc = $self->{pathnameencoding}) {
+               require Encode;
+               Encode::from_to($path, 'UTF-8', $enc);
+       }
        if ($self->{path_strip}) {
                $path =~ s!$self->{path_strip}!! or
                  die "Failed to strip path '$path' ($self->{path_strip})\n";
@@ -4521,6 +4540,10 @@ sub split_path {
 
 sub repo_path {
        my ($self, $path) = @_;
+       if (my $enc = $self->{pathnameencoding}) {
+               require Encode;
+               Encode::from_to($path, $enc, 'UTF-8');
+       }
        $self->{path_prefix}.(defined $path ? $path : '');
 }
 
diff --git a/git.c b/git.c
index b83c1d17ab803ae0fa1d6c0dece43f2c0334f23d..12d2952e079103c87b1f176f6928de4f342de9b5 100644 (file)
--- a/git.c
+++ b/git.c
@@ -188,7 +188,8 @@ static int handle_alias(int *argcp, const char ***argv)
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
-                       die("Bad alias.%s string", alias_command);
+                       die("Bad alias.%s string: %s", alias_command,
+                           split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
index 84261bba34dcdf436bb3b87ab88c67353ea4f4dd..d0687f45814eb72da5d8b59c49d86cced01c8715 100755 (executable)
@@ -232,6 +232,29 @@ sub evaluate_uri {
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+       #'Program' => 'py',
+       #'Library' => 'py',
+       'SConstruct' => 'py', # SCons equivalent of Makefile
+       'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+       # main extensions, defining name of syntax;
+       # see files in /usr/share/highlight/langDefs/ directory
+       map { $_ => $_ }
+               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+       # alternate extensions, see /etc/highlight/filetypes.conf
+       'h' => 'c',
+       map { $_ => 'cpp' } qw(cxx c++ cc),
+       map { $_ => 'php' } qw(php3 php4),
+       map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
+       'mak' => 'make',
+       map { $_ => 'xml' } qw(xhtml html htm),
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -1037,8 +1060,12 @@ sub run_request {
        reset_timer();
 
        evaluate_uri();
+       evaluate_gitweb_config();
        check_loadavg();
 
+       # $projectroot and $projects_list might be set in gitweb config file
+       $projects_list ||= $projectroot;
+
        evaluate_query_params();
        evaluate_path_info();
        evaluate_and_validate_params();
@@ -1086,12 +1113,8 @@ sub evaluate_argv {
 
 sub run {
        evaluate_argv();
-       evaluate_gitweb_config();
        evaluate_git_version();
 
-       # $projectroot and $projects_list might be set in gitweb config file
-       $projects_list ||= $projectroot;
-
        $pre_listen_hook->()
                if $pre_listen_hook;
 
@@ -1102,7 +1125,7 @@ sub run {
 
                run_request();
 
-               $pre_dispatch_hook->()
+               $post_dispatch_hook->()
                        if $post_dispatch_hook;
 
                last REQUEST if ($is_last_request->());
@@ -3316,30 +3339,6 @@ sub blob_contenttype {
 sub guess_file_syntax {
        my ($highlight, $mimetype, $file_name) = @_;
        return undef unless ($highlight && defined $file_name);
-
-       # configuration for 'highlight' (http://www.andre-simon.de/)
-       # match by basename
-       my %highlight_basename = (
-               #'Program' => 'py',
-               #'Library' => 'py',
-               'SConstruct' => 'py', # SCons equivalent of Makefile
-               'Makefile' => 'make',
-       );
-       # match by extension
-       my %highlight_ext = (
-               # main extensions, defining name of syntax;
-               # see files in /usr/share/highlight/langDefs/ directory
-               map { $_ => $_ }
-                       qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
-               # alternate extensions, see /etc/highlight/filetypes.conf
-               'h' => 'c',
-               map { $_ => 'cpp' } qw(cxx c++ cc),
-               map { $_ => 'php' } qw(php3 php4),
-               map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
-               'mak' => 'make',
-               map { $_ => 'xml' } qw(xhtml html htm),
-       );
-
        my $basename = basename($file_name, '.in');
        return $highlight_basename{$basename}
                if exists $highlight_basename{$basename};
diff --git a/graph.c b/graph.c
index 85ab150787bfcdd593ad13e7c00396a1c96c134d..f1a63c2241a30cce0141b87b5e8005b1a899a8ef 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -7,17 +7,6 @@
 
 /* Internal API */
 
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
 /*
  * Output a padding line in the graph.
  * This is similar to graph_next_line().  However, it is guaranteed to
@@ -73,7 +62,7 @@ enum graph_state {
 /*
  * The list of available column colors.
  */
-static char column_colors[][COLOR_MAXLEN] = {
+static const char *column_colors_ansi[] = {
        GIT_COLOR_RED,
        GIT_COLOR_GREEN,
        GIT_COLOR_YELLOW,
@@ -86,23 +75,33 @@ static char column_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_BOLD_BLUE,
        GIT_COLOR_BOLD_MAGENTA,
        GIT_COLOR_BOLD_CYAN,
+       GIT_COLOR_RESET,
 };
 
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+#define COLUMN_COLORS_ANSI_MAX (ARRAY_SIZE(column_colors_ansi) - 1)
+
+static const char **column_colors;
+static unsigned short column_colors_max;
 
-static const char *column_get_color_code(const struct column *c)
+void graph_set_column_colors(const char **colors, unsigned short colors_max)
 {
-       return column_colors[c->color];
+       column_colors = colors;
+       column_colors_max = colors_max;
+}
+
+static const char *column_get_color_code(unsigned short color)
+{
+       return column_colors[color];
 }
 
 static void strbuf_write_column(struct strbuf *sb, const struct column *c,
                                char col_char)
 {
-       if (c->color < COLUMN_COLORS_MAX)
-               strbuf_addstr(sb, column_get_color_code(c));
+       if (c->color < column_colors_max)
+               strbuf_addstr(sb, column_get_color_code(c->color));
        strbuf_addch(sb, col_char);
-       if (c->color < COLUMN_COLORS_MAX)
-               strbuf_addstr(sb, GIT_COLOR_RESET);
+       if (c->color < column_colors_max)
+               strbuf_addstr(sb, column_get_color_code(column_colors_max));
 }
 
 struct git_graph {
@@ -226,6 +225,11 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void
 struct git_graph *graph_init(struct rev_info *opt)
 {
        struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+
+       if (!column_colors)
+               graph_set_column_colors(column_colors_ansi,
+                                       COLUMN_COLORS_ANSI_MAX);
+
        graph->commit = NULL;
        graph->revs = opt;
        graph->num_parents = 0;
@@ -242,7 +246,7 @@ struct git_graph *graph_init(struct rev_info *opt)
         * always increment it for the first commit we output.
         * This way we start at 0 for the first commit.
         */
-       graph->default_column_color = COLUMN_COLORS_MAX - 1;
+       graph->default_column_color = column_colors_max - 1;
 
        /*
         * Allocate a reasonably large default number of columns
@@ -365,7 +369,7 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph)
 static unsigned short graph_get_current_column_color(const struct git_graph *graph)
 {
        if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
-               return COLUMN_COLORS_MAX;
+               return column_colors_max;
        return graph->default_column_color;
 }
 
@@ -375,7 +379,7 @@ static unsigned short graph_get_current_column_color(const struct git_graph *gra
 static void graph_increment_column_color(struct git_graph *graph)
 {
        graph->default_column_color = (graph->default_column_color + 1) %
-               COLUMN_COLORS_MAX;
+               column_colors_max;
 }
 
 static unsigned short graph_find_commit_color(const struct git_graph *graph,
@@ -1143,7 +1147,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                graph_update_state(graph, GRAPH_PADDING);
 }
 
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
        switch (graph->state) {
        case GRAPH_PADDING:
diff --git a/graph.h b/graph.h
index b82ae87a491454aa119476ac9609ad176e8c9406..aff960c7e8f63f49e3ec16afb0b891cf257ce999 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -4,6 +4,23 @@
 /* A graph is a pointer to this opaque structure */
 struct git_graph;
 
+/*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
 /*
  * Create a new struct git_graph.
  */
@@ -32,6 +49,17 @@ void graph_update(struct git_graph *graph, struct commit *commit);
  */
 int graph_is_commit_finished(struct git_graph const *graph);
 
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
 
 /*
  * graph_show_*: helper functions for printing to stdout
diff --git a/http.c b/http.c
index 1320c50e32eb7b8715b263bc2af089c3dbce39fa..0a5011f615bdfade0b69682b177dc97471ac20a3 100644 (file)
--- a/http.c
+++ b/http.c
@@ -41,6 +41,7 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static char *user_name, *user_pass;
+static const char *user_agent;
 
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
@@ -196,6 +197,9 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.useragent", var))
+               return git_config_string(&user_agent, var, value);
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -279,7 +283,8 @@ static CURL *get_curl_handle(void)
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
 
-       curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
+       curl_easy_setopt(result, CURLOPT_USERAGENT,
+               user_agent ? user_agent : GIT_HTTP_USER_AGENT);
 
        if (curl_ftp_no_epsv)
                curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
@@ -380,6 +385,8 @@ void http_init(struct remote *remote)
 #endif
        set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
+       set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
+
        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
        if (low_speed_limit != NULL)
                curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
index fb6aa4a551802de07be76bd838b6f22a236457ff..638076ec6ecde537b51041d1bdf4ef5a00a4b3cc 100644 (file)
@@ -20,6 +20,7 @@
 #include "attr.h"
 #include "merge-recursive.h"
 #include "dir.h"
+#include "submodule.h"
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
@@ -136,16 +137,10 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                if (parse_commit(commit) != 0)
                        printf("(bad commit)\n");
                else {
-                       const char *s;
-                       int len;
-                       for (s = commit->buffer; *s; s++)
-                               if (*s == '\n' && s[1] == '\n') {
-                                       s += 2;
-                                       break;
-                               }
-                       for (len = 0; s[len] && '\n' != s[len]; len++)
-                               ; /* do nothing */
-                       printf("%.*s\n", len, s);
+                       const char *title;
+                       int len = find_commit_subject(commit->buffer, &title);
+                       if (len)
+                               printf("%.*s\n", len, title);
                }
        }
 }
@@ -185,7 +180,7 @@ static int git_merge_trees(int index_only,
        opts.fn = threeway_merge;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
-       opts.msgs = get_porcelain_error_msgs();
+       set_porcelain_error_msgs(opts.msgs, "merge");
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -525,13 +520,15 @@ static void update_file_flags(struct merge_options *o,
                void *buf;
                unsigned long size;
 
-               if (S_ISGITLINK(mode))
+               if (S_ISGITLINK(mode)) {
                        /*
                         * We may later decide to recursively descend into
                         * the submodule directory and update its index
                         * and/or work tree, but we do not do that now.
                         */
+                       update_wd = 0;
                        goto update_index;
+               }
 
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
@@ -716,8 +713,8 @@ static struct merge_file_info merge_file(struct merge_options *o,
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result.clean = 0;
-                       hashcpy(result.sha, a->sha1);
+                       result.clean = merge_submodule(result.sha, one->path, one->sha1,
+                                                      a->sha1, b->sha1);
                } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
 
@@ -806,7 +803,8 @@ static int process_renames(struct merge_options *o,
                           struct string_list *b_renames)
 {
        int clean_merge = 1, i, j;
-       struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+       struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
+       struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
        const struct rename *sre;
 
        for (i = 0; i < a_renames->nr; i++) {
@@ -1178,26 +1176,48 @@ static int process_entry(struct merge_options *o,
        return clean_merge;
 }
 
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
+void set_porcelain_error_msgs(const char **msgs, const char *cmd)
 {
-       struct unpack_trees_error_msgs msgs = {
-               /* would_overwrite */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_file */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_dir */
-               "Updating '%s' would lose untracked files in it.  Aborting.",
-               /* would_lose_untracked */
-               "Untracked working tree file '%s' would be %s by merge.  Aborting",
-               /* bind_overlap -- will not happen here */
-               NULL,
-       };
-       if (advice_commit_before_merge) {
-               msgs.would_overwrite = msgs.not_uptodate_file =
-                       "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
-                       "Please, commit your changes or stash them before you can merge.";
-       }
-       return msgs;
+       const char *msg;
+       char *tmp;
+       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+       if (advice_commit_before_merge)
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
+                       "Please, commit your changes or stash them before you can %s.";
+       else
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
+       sprintf(tmp, msg, cmd, cmd2);
+       msgs[ERROR_WOULD_OVERWRITE] = tmp;
+       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+
+       msgs[ERROR_NOT_UPTODATE_DIR] =
+               "Updating the following directories would lose untracked files in it:\n%s";
+
+       if (advice_commit_before_merge)
+               msg = "The following untracked working tree files would be %s by %s:\n%%s"
+                       "Please move or remove them before you can %s.";
+       else
+               msg = "The following untracked working tree files would be %s by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "overwritten", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+       /*
+        * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
+        * cannot easily display it as a list.
+        */
+       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+
+       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
+               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
+               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
+               "The following Working tree files would be removed by sparse checkout update:\n%s";
 }
 
 int merge_trees(struct merge_options *o,
index b831293b3865ae52584565039e4a893c3c6f45ff..08f985036781f67eeda3fde1ae35f62f42f31096 100644 (file)
@@ -23,8 +23,11 @@ struct merge_options {
        struct string_list current_directory_set;
 };
 
-/* Return a list of user-friendly error messages to be used by merge */
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void);
+/*
+ * Sets the list of user-friendly error messages to be used by the
+ * command "cmd" (either merge or checkout)
+ */
+void set_porcelain_error_msgs(const char **msgs, const char *cmd);
 
 /* merge_trees() but with recursive ancestor consolidation */
 int merge_recursive(struct merge_options *o,
diff --git a/notes.c b/notes.c
index 197824439826881fc0de3275353ec1b74d86cfa3..7fd203560aaded5ebbd80d67c805df5a707aa60d 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -877,14 +877,6 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
        strbuf_release(&globbuf);
 }
 
-static int string_list_add_refs_from_list(struct string_list_item *item,
-                                         void *cb)
-{
-       struct string_list *list = cb;
-       string_list_add_refs_by_glob(list, item->string);
-       return 0;
-}
-
 static int notes_display_config(const char *k, const char *v, void *cb)
 {
        int *load_refs = cb;
@@ -947,30 +939,18 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
        load_subtree(t, &root_tree, t->root, 0);
 }
 
-struct load_notes_cb_data {
-       int counter;
-       struct notes_tree **trees;
-};
-
-static int load_one_display_note_ref(struct string_list_item *item,
-                                    void *cb_data)
-{
-       struct load_notes_cb_data *c = cb_data;
-       struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
-       init_notes(t, item->string, combine_notes_ignore, 0);
-       c->trees[c->counter++] = t;
-       return 0;
-}
-
 struct notes_tree **load_notes_trees(struct string_list *refs)
 {
+       struct string_list_item *item;
+       int counter = 0;
        struct notes_tree **trees;
-       struct load_notes_cb_data cb_data;
        trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
-       cb_data.counter = 0;
-       cb_data.trees = trees;
-       for_each_string_list(refs, load_one_display_note_ref, &cb_data);
-       trees[cb_data.counter] = NULL;
+       for_each_string_list_item(item, refs) {
+               struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
+               init_notes(t, item->string, combine_notes_ignore, 0);
+               trees[counter++] = t;
+       }
+       trees[counter] = NULL;
        return trees;
 }
 
@@ -995,10 +975,12 @@ void init_display_notes(struct display_notes_opt *opt)
 
        git_config(notes_display_config, &load_config_refs);
 
-       if (opt && opt->extra_notes_refs)
-               for_each_string_list(opt->extra_notes_refs,
-                                    string_list_add_refs_from_list,
-                                    &display_notes_refs);
+       if (opt && opt->extra_notes_refs) {
+               struct string_list_item *item;
+               for_each_string_list_item(item, opt->extra_notes_refs)
+                       string_list_add_refs_by_glob(&display_notes_refs,
+                                                    item->string);
+       }
 
        display_notes_trees = load_notes_trees(&display_notes_refs);
        string_list_clear(&display_notes_refs, 0);
index 82877c831ca7fe39abbb98805f5801bdc6fcac9c..4d1d61546f0441bd198cda3ad153b35a91444b72 100644 (file)
--- a/object.h
+++ b/object.h
@@ -21,6 +21,8 @@ struct object_array {
        } *objects;
 };
 
+#define OBJECT_ARRAY_INIT { 0, 0, NULL }
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
index 7f43f8ac3398fb2dbe393f08811989b0a8d4e2dd..1290570260d184b9c94fdab62650d6e474b365b4 100644 (file)
@@ -60,6 +60,37 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
        return 0;
 }
 
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+       char *p, *q;
+       int i;
+       p = name;
+       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+               while (*p && *p != '/')
+                       p++;
+               /* tolerate duplicate slashes; see check_ref_format() */
+               while (*p == '/')
+                       p++;
+       }
+       for (q = p; *q; q++)
+               ;
+       while (1) {
+               while (q > p && *q != '/')
+                       q--;
+               while (q > p && *(q-1) == '/')
+                       q--;
+               if (q == p)
+                       break;
+               *q = '\0';
+               if (rmdir(git_path("%s", name)))
+                       break;
+       }
+}
+
 /* make sure nobody touched the ref, and unlink */
 static void prune_ref(struct ref_to_prune *r)
 {
@@ -68,6 +99,7 @@ static void prune_ref(struct ref_to_prune *r)
        if (lock) {
                unlink_or_warn(git_path("%s", r->name));
                unlock_ref(lock);
+               try_remove_empty_parents(r->name);
        }
 }
 
diff --git a/path.c b/path.c
index 6b23023095d7e1a5cfc1aef8db6d6e5fb56b32de..a2c9d1e24afb9d1250c846791607c49d82e9565e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -122,6 +122,44 @@ char *git_path(const char *fmt, ...)
        return cleanup_path(pathname);
 }
 
+char *git_path_submodule(const char *path, const char *fmt, ...)
+{
+       char *pathname = get_pathname();
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir;
+       va_list args;
+       unsigned len;
+
+       len = strlen(path);
+       if (len > PATH_MAX-100)
+               return bad_path;
+
+       strbuf_addstr(&buf, path);
+       if (len && path[len-1] != '/')
+               strbuf_addch(&buf, '/');
+       strbuf_addstr(&buf, ".git");
+
+       git_dir = read_gitfile_gently(buf.buf);
+       if (git_dir) {
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, git_dir);
+       }
+       strbuf_addch(&buf, '/');
+
+       if (buf.len >= PATH_MAX)
+               return bad_path;
+       memcpy(pathname, buf.buf, buf.len + 1);
+
+       strbuf_release(&buf);
+       len = strlen(pathname);
+
+       va_start(args, fmt);
+       len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+       va_end(args);
+       if (len >= PATH_MAX)
+               return bad_path;
+       return cleanup_path(pathname);
+}
 
 /* git_mkstemp() - create tmp file honoring TMPDIR variable */
 int git_mkstemp(char *path, size_t len, const char *template)
index 4ab21d61b808d2e396419fb41419bcea7ab3cde4..a2ffb6402d45420dff4dcd545dfa08b57305d8cd 100644 (file)
@@ -38,7 +38,7 @@ $(makfile): ../GIT-CFLAGS Makefile
        echo '  echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-       $(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+       $(PERL_PATH) $< PREFIX='$(prefix_SQ)' INSTALL_BASE=''
 endif
 
 # this is just added comfort for calling make directly in perl dir
index b515fa2de332cc570a8a32861bd8d6491b61133e..a03fabf060081e9fa5e0d753cd0d9cf4bca5062e 100644 (file)
@@ -90,7 +90,7 @@ static void walk_commit_list(struct rev_info *revs)
 {
        int i;
        struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
+       struct object_array objects = OBJECT_ARRAY_INIT;
 
        /* Walk all commits, process their trees */
        while ((commit = get_revision(revs)) != NULL)
diff --git a/refs.c b/refs.c
index b5400674d7a1fcf4cc16dd630b8671b2ad7b9f7b..e3c05110e58ca5684f9c43b1e1a5eb2587c96828 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -157,7 +157,7 @@ static struct cached_refs {
        char did_packed;
        struct ref_list *loose;
        struct ref_list *packed;
-} cached_refs;
+} cached_refs, submodule_refs;
 static struct ref_list *current_ref;
 
 static struct ref_list *extra_refs;
@@ -229,23 +229,45 @@ void clear_extra_refs(void)
        extra_refs = NULL;
 }
 
-static struct ref_list *get_packed_refs(void)
+static struct ref_list *get_packed_refs(const char *submodule)
 {
-       if (!cached_refs.did_packed) {
-               FILE *f = fopen(git_path("packed-refs"), "r");
-               cached_refs.packed = NULL;
+       const char *packed_refs_file;
+       struct cached_refs *refs;
+
+       if (submodule) {
+               packed_refs_file = git_path_submodule(submodule, "packed-refs");
+               refs = &submodule_refs;
+               free_ref_list(refs->packed);
+       } else {
+               packed_refs_file = git_path("packed-refs");
+               refs = &cached_refs;
+       }
+
+       if (!refs->did_packed || submodule) {
+               FILE *f = fopen(packed_refs_file, "r");
+               refs->packed = NULL;
                if (f) {
-                       read_packed_refs(f, &cached_refs);
+                       read_packed_refs(f, refs);
                        fclose(f);
                }
-               cached_refs.did_packed = 1;
+               refs->did_packed = 1;
        }
-       return cached_refs.packed;
+       return refs->packed;
 }
 
-static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+static struct ref_list *get_ref_dir(const char *submodule, const char *base,
+                                   struct ref_list *list)
 {
-       DIR *dir = opendir(git_path("%s", base));
+       DIR *dir;
+       const char *path;
+
+       if (submodule)
+               path = git_path_submodule(submodule, "%s", base);
+       else
+               path = git_path("%s", base);
+
+
+       dir = opendir(path);
 
        if (dir) {
                struct dirent *de;
@@ -261,6 +283,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        struct stat st;
                        int flag;
                        int namelen;
+                       const char *refdir;
 
                        if (de->d_name[0] == '.')
                                continue;
@@ -270,16 +293,27 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        if (has_extension(de->d_name, ".lock"))
                                continue;
                        memcpy(ref + baselen, de->d_name, namelen+1);
-                       if (stat(git_path("%s", ref), &st) < 0)
+                       refdir = submodule
+                               ? git_path_submodule(submodule, "%s", ref)
+                               : git_path("%s", ref);
+                       if (stat(refdir, &st) < 0)
                                continue;
                        if (S_ISDIR(st.st_mode)) {
-                               list = get_ref_dir(ref, list);
+                               list = get_ref_dir(submodule, ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag)) {
+                       if (submodule) {
                                hashclr(sha1);
-                               flag |= REF_BROKEN;
-                       }
+                               flag = 0;
+                               if (resolve_gitlink_ref(submodule, ref, sha1) < 0) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
+                       } else
+                               if (!resolve_ref(ref, sha1, 1, &flag)) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
@@ -322,10 +356,16 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-static struct ref_list *get_loose_refs(void)
+static struct ref_list *get_loose_refs(const char *submodule)
 {
+       if (submodule) {
+               free_ref_list(submodule_refs.loose);
+               submodule_refs.loose = get_ref_dir(submodule, "refs", NULL);
+               return submodule_refs.loose;
+       }
+
        if (!cached_refs.did_loose) {
-               cached_refs.loose = get_ref_dir("refs", NULL);
+               cached_refs.loose = get_ref_dir(NULL, "refs", NULL);
                cached_refs.did_loose = 1;
        }
        return cached_refs.loose;
@@ -459,7 +499,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                git_snpath(path, sizeof(path), "%s", ref);
                /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
-                       struct ref_list *list = get_packed_refs();
+                       struct ref_list *list = get_packed_refs(NULL);
                        while (list) {
                                if (!strcmp(ref, list->name)) {
                                        hashcpy(sha1, list->sha1);
@@ -588,7 +628,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
                return -1;
 
        if ((flag & REF_ISPACKED)) {
-               struct ref_list *list = get_packed_refs();
+               struct ref_list *list = get_packed_refs(NULL);
 
                while (list) {
                        if (!strcmp(list->name, ref)) {
@@ -615,12 +655,12 @@ int peel_ref(const char *ref, unsigned char *sha1)
        return -1;
 }
 
-static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-                          int flags, void *cb_data)
+static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
+                          int trim, int flags, void *cb_data)
 {
        int retval = 0;
-       struct ref_list *packed = get_packed_refs();
-       struct ref_list *loose = get_loose_refs();
+       struct ref_list *packed = get_packed_refs(submodule);
+       struct ref_list *loose = get_loose_refs(submodule);
 
        struct ref_list *extra;
 
@@ -657,24 +697,54 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
        return retval;
 }
 
-int head_ref(each_ref_fn fn, void *cb_data)
+
+static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
        unsigned char sha1[20];
        int flag;
 
+       if (submodule) {
+               if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
+                       return fn("HEAD", sha1, 0, cb_data);
+
+               return 0;
+       }
+
        if (resolve_ref("HEAD", sha1, 1, &flag))
                return fn("HEAD", sha1, flag, cb_data);
+
        return 0;
 }
 
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(NULL, fn, cb_data);
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(submodule, fn, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -682,19 +752,34 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
+int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+}
+
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
        return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
+int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+}
+
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
        return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
+int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+}
+
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
+       return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
 }
 
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
@@ -734,7 +819,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0,
+       return do_for_each_ref(NULL, "refs/", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -958,7 +1043,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(ref, NULL, get_packed_refs(), 0)) {
+            !is_refname_available(ref, NULL, get_packed_refs(NULL), 0)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -1021,7 +1106,7 @@ static int repack_without_ref(const char *refname)
        int fd;
        int found = 0;
 
-       packed_ref_list = get_packed_refs();
+       packed_ref_list = get_packed_refs(NULL);
        for (list = packed_ref_list; list; list = list->next) {
                if (!strcmp(refname, list->name)) {
                        found = 1;
@@ -1119,10 +1204,10 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (!symref)
                return error("refname %s not found", oldref);
 
-       if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0))
                return 1;
 
-       if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0))
                return 1;
 
        lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
diff --git a/refs.h b/refs.h
index 762ce504b5eba3aacff204321a7c41db2b1e6053..5e7a9a59f5f6b687d15eb37a11696433bdc8f15c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -28,6 +28,14 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 
+extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data);
+extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
        return strpbrk(pattern, "?*[");
index afbba47460c0204721d61800033ed0a9f93f92bc..9143ec7a1772c45b6c3d4c84f6fc000040400dee 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -754,7 +754,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
 
 void ref_remove_duplicates(struct ref *ref_map)
 {
-       struct string_list refs = { NULL, 0, 0, 0 };
+       struct string_list refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *item = NULL;
        struct ref *prev = NULL, *next = NULL;
        for (; ref_map; prev = ref_map, ref_map = next) {
@@ -1704,7 +1704,7 @@ static int get_stale_heads_cb(const char *refname,
 struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
 {
        struct ref *ref, *stale_refs = NULL;
-       struct string_list ref_names = { NULL, 0, 0, 0 };
+       struct string_list ref_names = STRING_LIST_INIT_NODUP;
        struct stale_heads_info info;
        info.remote = remote;
        info.ref_names = &ref_names;
index d03a69634b81e1146c20182670a400b0c29b275f..78bbcf140346f14bbb961ed5e39e5752568b8d95 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -426,8 +426,8 @@ static int update_paths(struct string_list *update)
 
 static int do_plain_rerere(struct string_list *rr, int fd)
 {
-       struct string_list conflict = { NULL, 0, 0, 1 };
-       struct string_list update = { NULL, 0, 0, 1 };
+       struct string_list conflict = STRING_LIST_INIT_DUP;
+       struct string_list update = STRING_LIST_INIT_DUP;
        int i;
 
        find_conflict(&conflict);
@@ -547,7 +547,7 @@ int setup_rerere(struct string_list *merge_rr, int flags)
 
 int rerere(int flags)
 {
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd;
 
        fd = setup_rerere(&merge_rr, flags);
@@ -585,8 +585,8 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
 int rerere_forget(const char **pathspec)
 {
        int i, fd;
-       struct string_list conflict = { NULL, 0, 0, 1 };
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       struct string_list conflict = STRING_LIST_INIT_DUP;
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
 
        if (read_cache() < 0)
                return error("Could not read index");
index 174ebec9e573b1942e379942037f38500bd19b97..72b46125b719861641a55ee8fd534e0d1f47a94f 100644 (file)
@@ -28,29 +28,25 @@ void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
        ui->mode[stage - 1] = ce->ce_mode;
 }
 
-static int write_one(struct string_list_item *item, void *cbdata)
+void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
 {
-       struct strbuf *sb = cbdata;
-       struct resolve_undo_info *ui = item->util;
-       int i;
+       struct string_list_item *item;
+       for_each_string_list_item(item, resolve_undo) {
+               struct resolve_undo_info *ui = item->util;
+               int i;
 
-       if (!ui)
-               return 0;
-       strbuf_addstr(sb, item->string);
-       strbuf_addch(sb, 0);
-       for (i = 0; i < 3; i++)
-               strbuf_addf(sb, "%o%c", ui->mode[i], 0);
-       for (i = 0; i < 3; i++) {
-               if (!ui->mode[i])
+               if (!ui)
                        continue;
-               strbuf_add(sb, ui->sha1[i], 20);
+               strbuf_addstr(sb, item->string);
+               strbuf_addch(sb, 0);
+               for (i = 0; i < 3; i++)
+                       strbuf_addf(sb, "%o%c", ui->mode[i], 0);
+               for (i = 0; i < 3; i++) {
+                       if (!ui->mode[i])
+                               continue;
+                       strbuf_add(sb, ui->sha1[i], 20);
+               }
        }
-       return 0;
-}
-
-void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
-{
-   for_each_string_list(resolve_undo, write_one, sb);
 }
 
 struct string_list *resolve_undo_read(const char *data, unsigned long size)
index 7e82efd9324e84582732485c53c6c25a24c29997..b1c18906badc5fc4ce3af48daa7c226365e3c08f 100644 (file)
@@ -820,12 +820,12 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-               int (*for_each)(each_ref_fn, void *))
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+               int (*for_each)(const char *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        init_all_refs_cb(&cb, revs, flags);
-       for_each(handle_one_ref, &cb);
+       for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -1148,6 +1148,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                               int *unkc, const char **unkv)
 {
        const char *arg = argv[0];
+       const char *optarg;
+       int argcount;
 
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1160,11 +1162,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return 1;
        }
 
-       if (!prefixcmp(arg, "--max-count=")) {
-               revs->max_count = atoi(arg + 12);
+       if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+               revs->max_count = atoi(optarg);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--skip=")) {
-               revs->skip_count = atoi(arg + 7);
+               return argcount;
+       } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+               revs->skip_count = atoi(optarg);
+               return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
@@ -1178,18 +1182,24 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--max-age=")) {
-               revs->max_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--since=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--after=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--min-age=")) {
-               revs->min_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--before=")) {
-               revs->min_age = approxidate(arg + 9);
-       } else if (!prefixcmp(arg, "--until=")) {
-               revs->min_age = approxidate(arg + 8);
+       } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+               revs->max_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+               revs->min_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
        } else if (!strcmp(arg, "--ancestry-path")) {
@@ -1295,6 +1305,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+               /*
+                * Detached form ("--pretty X" as opposed to "--pretty=X")
+                * not allowed, since the argument is optional.
+                */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
@@ -1359,21 +1373,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
-       } else if (!strncmp(arg, "--date=", 7)) {
-               revs->date_mode = parse_date_format(arg + 7);
+       } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+               revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
+               return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
-       else if (!prefixcmp(arg, "--author=")) {
-               add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-       } else if (!prefixcmp(arg, "--committer=")) {
-               add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-       } else if (!prefixcmp(arg, "--grep=")) {
-               add_message_grep(revs, arg+7);
+       else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+               add_message_grep(revs, optarg);
+               return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
@@ -1382,12 +1400,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
-       } else if (!prefixcmp(arg, "--encoding=")) {
-               arg += 11;
-               if (strcmp(arg, "none"))
-                       git_log_output_encoding = xstrdup(arg);
+       } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+               if (strcmp(optarg, "none"))
+                       git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
+               return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@ -1417,14 +1435,14 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 }
 
-static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 }
 
 static void append_prune_data(const char ***prune_data, const char **av)
@@ -1466,6 +1484,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
+       const char *submodule = NULL;
+       const char *optarg;
+       int argcount;
+
+       if (opt)
+               submodule = opt->submodule;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1490,32 +1514,33 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        int opts;
 
                        if (!strcmp(arg, "--all")) {
-                               handle_refs(revs, flags, for_each_ref);
-                               handle_refs(revs, flags, head_ref);
+                               handle_refs(submodule, revs, flags, for_each_ref_submodule);
+                               handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               handle_refs(revs, flags, for_each_branch_ref);
+                               handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               handle_refs(revs, flags, for_each_bad_bisect_ref);
-                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               handle_refs(revs, flags, for_each_tag_ref);
+                               handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               handle_refs(revs, flags, for_each_remote_ref);
+                               handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
                                struct all_refs_cb cb;
+                               i += argcount - 1;
                                init_all_refs_cb(&cb, revs, flags);
-                               for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+                               for_each_glob_ref(handle_one_ref, optarg, &cb);
                                continue;
                        }
                        if (!prefixcmp(arg, "--branches=")) {
index 36fdf22b299c4cc7e6f20d767d5c6a6c3f69d952..05659c64acd7fe8eb7be011cee6174e397e57baf 100644 (file)
@@ -151,6 +151,7 @@ extern volatile show_early_output_fn_t show_early_output;
 struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+       const char *submodule;
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
index e42ef96d457f6aa12ab7d0057acf936db0335d40..0cd9435619f1e0637584289b45d52c1cdd8a9460 100644 (file)
@@ -2086,6 +2086,7 @@ void *read_sha1_file_repl(const unsigned char *sha1,
 {
        const unsigned char *repl = lookup_replace_object(sha1);
        void *data = read_object(repl, type, size);
+       char *path;
 
        /* die if we replaced an object with one that does not exist */
        if (!data && repl != sha1)
@@ -2093,8 +2094,16 @@ void *read_sha1_file_repl(const unsigned char *sha1,
                    sha1_to_hex(repl), sha1_to_hex(sha1));
 
        /* legacy behavior is to die on corrupted objects */
-       if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
-               die("object %s is corrupted", sha1_to_hex(repl));
+       if (!data) {
+               if (has_loose_object(repl)) {
+                       path = sha1_file_name(sha1);
+                       die("loose object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+               }
+               if (has_packed_and_bad(repl)) {
+                       path = sha1_pack_name(sha1);
+                       die("packed object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+               }
+       }
 
        if (replacement)
                *replacement = repl;
index 4d90eda19efe0a80c1cb39e8897ab3ed5e6fcf56..a0363dea203d2a06e985bb4e140ffc675428cbf8 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -47,7 +47,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
 {
        int i = 0, cur_depth = 0;
        struct commit_list *result = NULL;
-       struct object_array stack = {0, 0, NULL};
+       struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
 
        while (commit || i < heads->nr || stack.nr) {
index 680d600d16c095eb167be2eca8c1a7cb94f962af..494693898b89fe2f8145b483ea7e366e9b1192c1 100644 (file)
@@ -12,6 +12,9 @@ struct string_list
        unsigned int strdup_strings:1;
 };
 
+#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
+#define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1 }
+
 void print_string_list(const struct string_list *p, const char *text);
 void string_list_clear(struct string_list *list, int free_util);
 
@@ -20,10 +23,12 @@ void string_list_clear(struct string_list *list, int free_util);
 typedef void (*string_list_clear_func_t)(void *p, const char *str);
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
-/* Use this function to iterate over each item */
+/* Use this function or the macro below to iterate over each item */
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t, void *cb_data);
+#define for_each_string_list_item(item,list) \
+       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
 
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
index 61cb6e21ddfc789ce59597c96204e7904cd9359e..91a47587478ae0550be8f41c00cb1749c85834f4 100644 (file)
@@ -6,6 +6,11 @@
 #include "revision.h"
 #include "run-command.h"
 #include "diffcore.h"
+#include "refs.h"
+#include "string-list.h"
+
+struct string_list config_name_for_path;
+struct string_list config_ignore_for_name;
 
 static int add_submodule_odb(const char *path)
 {
@@ -46,16 +51,90 @@ static int add_submodule_odb(const char *path)
        return ret;
 }
 
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+                                            const char *path)
+{
+       struct string_list_item *path_option, *ignore_option;
+       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+       if (path_option) {
+               ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
+               if (ignore_option)
+                       handle_ignore_submodules_arg(diffopt, ignore_option->util);
+       }
+}
+
+static int submodule_config(const char *var, const char *value, void *cb)
+{
+       if (!prefixcmp(var, "submodule."))
+               return parse_submodule_config_option(var, value);
+       return 0;
+}
+
+void gitmodules_config(void)
+{
+       const char *work_tree = get_git_work_tree();
+       if (work_tree) {
+               struct strbuf gitmodules_path = STRBUF_INIT;
+               strbuf_addstr(&gitmodules_path, work_tree);
+               strbuf_addstr(&gitmodules_path, "/.gitmodules");
+               git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+               strbuf_release(&gitmodules_path);
+       }
+}
+
+int parse_submodule_config_option(const char *var, const char *value)
+{
+       int len;
+       struct string_list_item *config;
+       struct strbuf submodname = STRBUF_INIT;
+
+       var += 10;              /* Skip "submodule." */
+
+       len = strlen(var);
+       if ((len > 5) && !strcmp(var + len - 5, ".path")) {
+               strbuf_add(&submodname, var, len - 5);
+               config = unsorted_string_list_lookup(&config_name_for_path, value);
+               if (config)
+                       free(config->util);
+               else
+                       config = string_list_append(&config_name_for_path, xstrdup(value));
+               config->util = strbuf_detach(&submodname, NULL);
+               strbuf_release(&submodname);
+       } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+               if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
+                   strcmp(value, "all") && strcmp(value, "none")) {
+                       warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
+                       return 0;
+               }
+
+               strbuf_add(&submodname, var, len - 7);
+               config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
+               if (config)
+                       free(config->util);
+               else
+                       config = string_list_append(&config_ignore_for_name,
+                                                   strbuf_detach(&submodname, NULL));
+               strbuf_release(&submodname);
+               config->util = xstrdup(value);
+               return 0;
+       }
+       return 0;
+}
+
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
 {
+       DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
+       DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+       DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+
        if (!strcmp(arg, "all"))
                DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        else if (!strcmp(arg, "untracked"))
                DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
        else if (!strcmp(arg, "dirty"))
                DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
-       else
+       else if (strcmp(arg, "none"))
                die("bad --ignore-submodules argument: %s", arg);
 }
 
@@ -218,3 +297,163 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        strbuf_release(&buf);
        return dirty_submodule;
 }
+
+static int find_first_merges(struct object_array *result, const char *path,
+               struct commit *a, struct commit *b)
+{
+       int i, j;
+       struct object_array merges;
+       struct commit *commit;
+       int contains_another;
+
+       char merged_revision[42];
+       const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+                                  "--all", merged_revision, NULL };
+       struct rev_info revs;
+       struct setup_revision_opt rev_opts;
+
+       memset(&merges, 0, sizeof(merges));
+       memset(result, 0, sizeof(struct object_array));
+       memset(&rev_opts, 0, sizeof(rev_opts));
+
+       /* get all revisions that merge commit a */
+       snprintf(merged_revision, sizeof(merged_revision), "^%s",
+                       sha1_to_hex(a->object.sha1));
+       init_revisions(&revs, NULL);
+       rev_opts.submodule = path;
+       setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+
+       /* save all revisions from the above list that contain b */
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       while ((commit = get_revision(&revs)) != NULL) {
+               struct object *o = &(commit->object);
+               if (in_merge_bases(b, &commit, 1))
+                       add_object_array(o, NULL, &merges);
+       }
+
+       /* Now we've got all merges that contain a and b. Prune all
+        * merges that contain another found merge and save them in
+        * result.
+        */
+       for (i = 0; i < merges.nr; i++) {
+               struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+               contains_another = 0;
+               for (j = 0; j < merges.nr; j++) {
+                       struct commit *m2 = (struct commit *) merges.objects[j].item;
+                       if (i != j && in_merge_bases(m2, &m1, 1)) {
+                               contains_another = 1;
+                               break;
+                       }
+               }
+
+               if (!contains_another)
+                       add_object_array(merges.objects[i].item,
+                                        merges.objects[i].name, result);
+       }
+
+       free(merges.objects);
+       return result->nr;
+}
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode = DATE_NORMAL;
+       format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+       fprintf(stderr, "%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+#define MERGE_WARNING(path, msg) \
+       warning("Failed to merge submodule %s (%s)", path, msg);
+
+int merge_submodule(unsigned char result[20], const char *path,
+                   const unsigned char base[20], const unsigned char a[20],
+                   const unsigned char b[20])
+{
+       struct commit *commit_base, *commit_a, *commit_b;
+       int parent_count;
+       struct object_array merges;
+
+       int i;
+
+       /* store a in result in case we fail */
+       hashcpy(result, a);
+
+       /* we can not handle deletion conflicts */
+       if (is_null_sha1(base))
+               return 0;
+       if (is_null_sha1(a))
+               return 0;
+       if (is_null_sha1(b))
+               return 0;
+
+       if (add_submodule_odb(path)) {
+               MERGE_WARNING(path, "not checked out");
+               return 0;
+       }
+
+       if (!(commit_base = lookup_commit_reference(base)) ||
+           !(commit_a = lookup_commit_reference(a)) ||
+           !(commit_b = lookup_commit_reference(b))) {
+               MERGE_WARNING(path, "commits not present");
+               return 0;
+       }
+
+       /* check whether both changes are forward */
+       if (!in_merge_bases(commit_base, &commit_a, 1) ||
+           !in_merge_bases(commit_base, &commit_b, 1)) {
+               MERGE_WARNING(path, "commits don't follow merge-base");
+               return 0;
+       }
+
+       /* Case #1: a is contained in b or vice versa */
+       if (in_merge_bases(commit_a, &commit_b, 1)) {
+               hashcpy(result, b);
+               return 1;
+       }
+       if (in_merge_bases(commit_b, &commit_a, 1)) {
+               hashcpy(result, a);
+               return 1;
+       }
+
+       /*
+        * Case #2: There are one or more merges that contain a and b in
+        * the submodule. If there is only one, then present it as a
+        * suggestion to the user, but leave it marked unmerged so the
+        * user needs to confirm the resolution.
+        */
+
+       /* find commit which merges them */
+       parent_count = find_first_merges(&merges, path, commit_a, commit_b);
+       switch (parent_count) {
+       case 0:
+               MERGE_WARNING(path, "merge following commits not found");
+               break;
+
+       case 1:
+               MERGE_WARNING(path, "not fast-forward");
+               fprintf(stderr, "Found a possible merge resolution "
+                               "for the submodule:\n");
+               print_commit((struct commit *) merges.objects[0].item);
+               fprintf(stderr,
+                       "If this is correct simply add it to the index "
+                       "for example\n"
+                       "by using:\n\n"
+                       "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                       "which will accept this suggestion.\n",
+                       sha1_to_hex(merges.objects[0].item->sha1), path);
+               break;
+
+       default:
+               MERGE_WARNING(path, "multiple merges found");
+               for (i = 0; i < merges.nr; i++)
+                       print_commit((struct commit *) merges.objects[i].item);
+       }
+
+       free(merges.objects);
+       return 0;
+}
index 6fd3bb40702e5fb905eb4ae519d487e3c9f40073..386f410a66d9c431a61c0e26c773f1b3452f0d09 100644 (file)
@@ -3,11 +3,17 @@
 
 struct diff_options;
 
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+               const char *path);
+void gitmodules_config();
+int parse_submodule_config_option(const char *var, const char *value);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 void show_submodule_summary(FILE *f, const char *path,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule,
                const char *del, const char *add, const char *reset);
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
+int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
+                   const unsigned char a[20], const unsigned char b[20]);
 
 #endif
index 7dcbb232cd876cb7b976443cc586f60a94ab92bf..4e731dc1e3bef53903f030ee7c63fe7ef7324cb1 100644 (file)
@@ -1,2 +1,3 @@
 /trash directory*
 /test-results
+/.prove
index cf5f9e2e1eb17c442220464d06374e6c529f8629..819b936870300fc8325c0f24f8c68ea8bdaa2727 100644 (file)
@@ -30,6 +30,7 @@ clean:
        $(RM) -r 'trash directory'.* test-results
        $(RM) t????/cvsroot/CVSROOT/?*
        $(RM) -r valgrind/bin
+       $(RM) .prove
 
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
index 0d1183c3e69904e9e3543d757f14f10c629e199b..410499a09645634349d7685e728228f238021c34 100644 (file)
--- a/t/README
+++ b/t/README
@@ -467,6 +467,13 @@ library for your script to use.
    <expected> file.  This behaves like "cmp" but produces more
    helpful output when the test is run with "-v" option.
 
+ - test_path_is_file <file> [<diagnosis>]
+   test_path_is_dir <dir> [<diagnosis>]
+   test_path_is_missing <path> [<diagnosis>]
+
+   Check whether a file/directory exists or doesn't. <diagnosis> will
+   be displayed if the test fails.
+
  - test_when_finished <script>
 
    Prepend <script> to a list of commands to run to clean up
index 6aefe27593e89d57f075bc0d3dbc5a1104d874b7..6ccf7970916b58748aedcce7e583eed2dee782d3 100644 (file)
@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do
        case $line in
        squash|fixup|edit|reword)
                action="$line";;
+       exec*)
+               echo "$line" | sed 's/_/ /g' >> "$1";;
        "#")
                echo '# comment' >> "$1";;
        ">")
index 0c562bb820cb747a8f0d0df1eabb39da0b9eea21..93ca84f9e6d3dfd859d8bd4941f4d2c5a354ab6c 100755 (executable)
@@ -359,7 +359,7 @@ test_expect_success \
 
 test_expect_success \
     'a/b (untracked) vs a, plus c/d case test.' \
-    '! git read-tree -u -m "$treeH" "$treeM" &&
+    'test_must_fail git read-tree -u -m "$treeH" "$treeM" &&
      git ls-files --stage &&
      test -f a/b'
 
index 62246dbf9546b9ee5ad4dbeb0b34de45a1671ac2..9a07de1a5b6d3a98c0db0b24cdd9c3a68cef79b5 100755 (executable)
@@ -1,16 +1,30 @@
 #!/bin/sh
 
-test_description='sparse checkout tests'
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D    sub/added
+* (HEAD, tag: top) modified and added
+| M    init.t
+| A    sub/added
+* (tag: init) init
+  A    init.t
+'
 
 . ./test-lib.sh
 
-cat >expected <<EOF
-100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0      init.t
-100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      sub/added
-EOF
 test_expect_success 'setup' '
+       cat >expected <<-\EOF &&
+       100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0       init.t
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       sub/added
+       EOF
+       cat >expected.swt <<-\EOF &&
+       H init.t
+       H sub/added
+       EOF
+
        test_commit init &&
-       echo modified >> init.t &&
+       echo modified >>init.t &&
        mkdir sub &&
        touch sub/added &&
        git add init.t sub/added &&
@@ -20,26 +34,22 @@ test_expect_success 'setup' '
        git commit -m removed &&
        git tag removed &&
        git checkout top &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_success 'read-tree without .git/info/sparse-checkout' '
        git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result
 '
 
 test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
-       echo > .git/info/sparse-checkout
+       echo >.git/info/sparse-checkout
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -47,9 +57,9 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
 
 test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        git read-tree --no-sparse-checkout -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -57,94 +67,113 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
 
 test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        test_must_fail git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-S init.t
-H sub/added
-EOF
 test_expect_success 'match directories with trailing slash' '
+       cat >expected.swt-noinit <<-\EOF &&
+       S init.t
+       H sub/added
+       EOF
+
        echo sub/ > .git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
        git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_failure 'match directories without trailing slash' '
-       echo init.t > .git/info/sparse-checkout &&
-       echo sub >> .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
+       echo sub >>.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-S sub/added
-EOF
 test_expect_success 'checkout area changes' '
-       echo init.t > .git/info/sparse-checkout &&
+       cat >expected.swt-nosub <<-\EOF &&
+       H init.t
+       S sub/added
+       EOF
+
+       echo init.t >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-nosub result &&
        test -f init.t &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree updates worktree, absent case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
        git read-tree -m -u HEAD^ &&
        test ! -f init.t
 '
 
 test_expect_success 'read-tree updates worktree, dirty case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > init.t &&
+       echo dirty >init.t &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty init.t &&
        rm init.t
 '
 
 test_expect_success 'read-tree removes worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > added &&
+       echo dirty >added &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty added
 '
 
 test_expect_success 'read-tree adds to worktree, absent case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        git read-tree -u -m HEAD^ &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree adds to worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        mkdir sub &&
-       echo dirty > sub/added &&
+       echo dirty >sub/added &&
        git read-tree -u -m HEAD^ &&
        grep -q dirty sub/added
 '
 
+test_expect_success 'index removal and worktree narrowing at the same time' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       echo sub/added >>.git/info/sparse-checkout &&
+       git checkout -f top &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout removed &&
+       git ls-files sub/added >result &&
+       test ! -f sub/added &&
+       test_cmp empty result
+'
+
+test_expect_success 'read-tree --reset removes outside worktree' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout -f top &&
+       git reset --hard removed &&
+       git ls-files sub/added >result &&
+       test_cmp empty result
+'
+
 test_done
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755 (executable)
index 0000000..fa69016
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+#   1) we are on the specified branch, <branch>;
+#   2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+       exp_branch=$1 &&
+       exp_ref="refs/heads/$exp_branch" &&
+
+       # if <sha> is not specified, use HEAD.
+       exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+       # default options for git checkout: -b
+       if [ -z "$3" ]; then
+               opts="-b"
+       else
+               opts="$3"
+       fi
+
+       git checkout $opts $exp_branch $exp_sha &&
+
+       test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+       test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+       ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+       echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+       ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+       echo >file2 file2 &&
+       git add file2
+}
+
+test_expect_success 'setup' '
+       test_commit initial file1 &&
+       HEAD1=$(git rev-parse --verify HEAD) &&
+
+       test_commit change1 file1 &&
+       HEAD2=$(git rev-parse --verify HEAD) &&
+
+       git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+       do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+       git checkout branch1 &&
+       git branch -D branch2 &&
+
+       do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_unmergeable &&
+       test_must_fail do_checkout branch2 $HEAD1 &&
+       test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+       # still dirty and on branch1
+       do_checkout branch2 $HEAD1 "-f -b" &&
+       test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 &&
+       test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+       # clean up from previous test
+       git reset --hard &&
+
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 "-f -b" &&
+       test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+       git reset --hard HEAD &&
+
+       test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+       git checkout branch1 &&
+
+       do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+       git checkout $(git rev-parse --verify HEAD) &&
+
+       do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+       git checkout branch1 &&
+
+       do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+       git checkout branch1 &&
+
+       setup_dirty_unmergeable &&
+       test_must_fail do_checkout branch2 $HEAD1 -B &&
+       test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+       # still dirty and on branch1
+       do_checkout branch2 $HEAD1 "-f -B" &&
+       test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+       git checkout branch1 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 -B &&
+       test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+       # clean up from previous test
+       git reset --hard &&
+
+       git checkout branch1 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 "-f -B" &&
+       test_must_fail test_dirty_mergeable
+'
+
+test_done
index d541544537d47587abad8d27565cf9197387cfd6..efe2900a372601848248de33188bda292185ba40 100755 (executable)
@@ -294,7 +294,7 @@ test_expect_success 'fail if the index has unresolved entries' '
        grep "You have not concluded your merge" out &&
        rm -f .git/MERGE_HEAD &&
        test_must_fail git merge "$c5" 2> out &&
-       grep "Your local changes to .* would be overwritten by merge." out
+       grep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
index 525174013c4c33eab5bdbde8831d43f1ddbaeaae..cd04361df811d329e269ac1d5eb0b0f03baa74d8 100755 (executable)
@@ -60,6 +60,12 @@ test_expect_success 'see if git pack-refs --prune remove ref files' '
      ! test -f .git/refs/heads/f
 '
 
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+     git branch r/s/t &&
+     git pack-refs --all --prune &&
+     ! test -e .git/refs/heads/r
+'
+
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
     'git branch g/h &&
index 1d82f79ee07e8f9317bc23c49cdf941af36ce3ff..96b75813d73b3fd34a3c96aa9e977594319adb1d 100755 (executable)
@@ -299,7 +299,7 @@ cat expect-F >> expect-rm-F
 test_expect_success 'verify note removal with -F /dev/null' '
        git log -4 > output &&
        test_cmp expect-rm-F output &&
-       ! git notes show
+       test_must_fail git notes show
 '
 
 test_expect_success 'do not create empty note with -m "" (setup)' '
@@ -309,7 +309,7 @@ test_expect_success 'do not create empty note with -m "" (setup)' '
 test_expect_success 'verify non-creation of note with -m ""' '
        git log -4 > output &&
        test_cmp expect-rm-F output &&
-       ! git notes show
+       test_must_fail git notes show
 '
 
 cat > expect-combine_m_and_F << EOF
@@ -357,7 +357,7 @@ cat expect-multiline >> expect-rm-remove
 test_expect_success 'verify note removal with "git notes remove"' '
        git log -4 > output &&
        test_cmp expect-rm-remove output &&
-       ! git notes show HEAD^
+       test_must_fail git notes show HEAD^
 '
 
 cat > expect << EOF
index b4554041b49d1ab7b56430f2e85b76a23d360fb4..c4282179b387c75e084d05075a7fd365c3220cdc 100755 (executable)
@@ -67,7 +67,7 @@ test_expect_success 'remove some commits' '
 
 test_expect_success 'verify that commits are gone' '
 
-       ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
        git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
@@ -106,7 +106,7 @@ test_expect_success 'prune notes' '
 
 test_expect_success 'verify that notes are gone' '
 
-       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
        git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
@@ -130,8 +130,8 @@ test_expect_success 'prune -v notes' '
 
 test_expect_success 'verify that notes are gone' '
 
-       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
 
index a19aeb6441cb29dc20478cf898b5f7767d770c94..349eebd54268c927249322dc22fc478869860f7c 100755 (executable)
@@ -153,7 +153,8 @@ test_expect_success 'setup: recover' '
 test_expect_success 'Show verbose error when HEAD could not be detached' '
        >B &&
        test_must_fail git rebase topic 2>output.err >output.out &&
-       grep "Untracked working tree file .B. would be overwritten" output.err
+       grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+       grep B output.err
 '
 rm -f B
 
index 7b7d07269ae35f56dd02a223f350ae0da97bae85..2bea65634a1790ceecf824309c1fbb66250f58d5 100755 (executable)
@@ -74,6 +74,15 @@ test_expect_success 'rebase the other way' '
        git rebase --merge side
 '
 
+test_expect_success 'rebase -Xtheirs' '
+       git checkout -b conflicting master~2 &&
+       echo "AB $T" >> original &&
+       git commit -mconflicting original &&
+       git rebase -Xtheirs master &&
+       grep AB original &&
+       ! grep 11 original
+'
+
 test_expect_success 'merge and rebase should match' '
        git diff-tree -r test-rebase test-merge >difference &&
        if test -s difference
index 9f03ce699e8f4c3c654065deae675d1a7b259c74..af3b663aeee8354c6f8f2dff05666f6d70c07055 100755 (executable)
@@ -64,6 +64,67 @@ test_expect_success 'setup' '
        done
 '
 
+# "exec" commands are ran with the user shell by default, but this may
+# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
+# to create a file. Unseting SHELL avoids such non-portable behavior
+# in tests.
+SHELL=
+
+test_expect_success 'rebase -i with the exec command' '
+       git checkout master &&
+       (
+       FAKE_LINES="1 exec_>touch-one
+               2 exec_>touch-two exec_false exec_>touch-three
+               3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i A
+       ) &&
+       test_path_is_file touch-one &&
+       test_path_is_file touch-two &&
+       test_path_is_missing touch-three " (should have stopped before)" &&
+       test $(git rev-parse C) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of C)"
+               false
+       } &&
+       git rebase --continue &&
+       test_path_is_file touch-three &&
+       test_path_is_file "touch-file  name with spaces" &&
+       test_path_is_file touch-after-semicolon &&
+       test $(git rev-parse master) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of master)"
+               false
+       } &&
+       rm -f touch-*
+'
+
+test_expect_success 'rebase -i with the exec command runs from tree root' '
+       git checkout master &&
+       mkdir subdir && cd subdir &&
+       FAKE_LINES="1 exec_>touch-subdir" \
+               git rebase -i HEAD^ &&
+       cd .. &&
+       test_path_is_file touch-subdir &&
+       rm -fr subdir
+'
+
+test_expect_success 'rebase -i with the exec command checks tree cleanness' '
+       git checkout master &&
+       (
+       FAKE_LINES="exec_echo_foo_>file1 1" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i HEAD^
+       ) &&
+       test $(git rev-parse master^) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of master^)"
+               false
+       } &&
+       git reset --hard &&
+       git rebase --continue
+'
+
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
        git rebase -i F &&
@@ -143,16 +204,17 @@ test_expect_success 'abort' '
        git rebase --abort &&
        test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
-       ! test -d .git/rebase-merge
+       test_path_is_missing .git/rebase-merge
 '
 
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
        test_must_fail git rebase -i master > output 2>&1 &&
-       grep "Untracked working tree file .file1. would be overwritten" \
+       grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
-       ! test -d .git/rebase-merge &&
+       grep "file1" output &&
+       test_path_is_missing .git/rebase-merge &&
        git reset --hard HEAD^
 '
 
index 2999e78937f31a45e9e2ea925f69ac00f157503f..fbb3f2e0dfcf1a0673dbd2022a4ed843990fce52 100755 (executable)
@@ -38,7 +38,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                git rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
                test ! -d "$dotest"
@@ -49,7 +49,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                test_must_fail git rebase --skip &&
                test $(git rev-parse HEAD) = $(git rev-parse master) &&
                git rebase --abort &&
@@ -62,7 +62,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                echo c > a &&
                echo d >> a &&
                git add a &&
index c49143a1a45d6949253e2daf0e57f60029041e60..6f73b95558c85264d073d42f67c407967c4d5c69 100755 (executable)
@@ -43,11 +43,11 @@ test_expect_success 'setup' '
 # G2 = same changes as G
 test_expect_success 'skip same-resolution merges with -p' '
        git checkout H &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit L file1 23 &&
        git checkout I &&
        test_commit G2 file1 3 &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit J file1 23 &&
        test_commit K file7 file7 &&
        git rebase -i -p L &&
@@ -65,11 +65,11 @@ test_expect_success 'skip same-resolution merges with -p' '
 # G2 = different changes as G
 test_expect_success 'keep different-resolution merges with -p' '
        git checkout H &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit L2 file1 23 &&
        git checkout I &&
        test_commit G3 file1 4 &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit J2 file1 24 &&
        test_commit K2 file7 file7 &&
        test_must_fail git rebase -i -p L2 &&
index e51e505a9fb902ec7d4cedfa32052f03a04e612e..c10b28cf5731705b437793a58f5acd6c605ad579 100755 (executable)
@@ -13,11 +13,29 @@ test_expect_success setup '
 
        git checkout -b empty-branch &&
        test_tick &&
-       git commit --allow-empty -m "empty"
+       git commit --allow-empty -m "empty" &&
+
+       echo third >> file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit --allow-empty-message -m ""
 
 '
 
 test_expect_success 'cherry-pick an empty commit' '
+       git checkout master && {
+               git cherry-pick empty-branch^
+               test "$?" = 1
+       }
+'
+
+test_expect_success 'index lockfile was removed' '
+
+       test ! -f .git/index.lock
+
+'
+
+test_expect_success 'cherry-pick a commit with an empty message' '
        git checkout master && {
                git cherry-pick empty-branch
                test "$?" = 1
index dae635851666a7f5ccf685b89d64f31d5dbdd2fd..19857f4326aa491a4c7d292900a0a766e7c45f80 100755 (executable)
@@ -208,6 +208,7 @@ log -p --first-parent master
 log -m -p --first-parent master
 log -m -p master
 log -SF master
+log -S F master
 log -SF -p master
 log --decorate --all
 log --decorate=full --all
@@ -282,4 +283,8 @@ diff master master^ side
 diff --dirstat master~1 master~2
 EOF
 
+test_expect_success 'log -S requires an argument' '
+       test_must_fail git log -S
+'
+
 test_done
diff --git a/t/t4013/diff.log_-S_F_master b/t/t4013/diff.log_-S_F_master
new file mode 100644 (file)
index 0000000..978d2b4
--- /dev/null
@@ -0,0 +1,7 @@
+$ git log -S F master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+$
index 5b10e976a3fd0c554ff2fa14004e69343a9ec634..61de8a2718df8a7b0b0661d7ba47ade133d66ab8 100755 (executable)
@@ -32,7 +32,7 @@ EOF
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
+builtin_patterns="bibtex cpp csharp html java objc pascal php python ruby tex"
 for p in $builtin_patterns
 do
        test_expect_success "builtin $p pattern compiles" '
index 1bd8e5ee3ac5ca4d101738048e6769ae798b23dc..d99814ac641c93383d7199f4419083bedcfc9ea8 100755 (executable)
@@ -114,6 +114,69 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
        ! test -s actual4
 '
 
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+       git config diff.ignoreSubmodules all &&
+       git diff HEAD >actual &&
+       ! test -s actual &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore all &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       sed -e "1,/^@@/d" actual3 >actual3.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual3.body &&
+       git config submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git diff HEAD --ignore-submodules=none >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+       git config diff.ignoreSubmodules dirty &&
+       git diff HEAD >actual &&
+       ! test -s actual &&
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config -f .gitmodules submodule.subname.ignore all &&
+       git config -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config -f .gitmodules submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       sed -e "1,/^@@/d" actual3 >actual3.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual3.body &&
+       git config -f .gitmodules submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       git config --unset diff.ignoreSubmodules &&
+       rm .gitmodules
+'
+
 test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
        (
                cd sub &&
@@ -146,6 +209,103 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
        ! test -s actual4
 '
 
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+       git config submodule.subname.ignore all &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       ! test -s actual3 &&
+       git config submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git diff --ignore-submodules=none HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+       git config --add -f .gitmodules submodule.subname.ignore all &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config -f .gitmodules submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       ! test -s actual3 &&
+       git config -f .gitmodules submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git diff --ignore-submodules HEAD^..HEAD >actual &&
+       ! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore dirty &&
+       git config submodule.subname.path sub &&
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore all &&
+       git diff HEAD^..HEAD >actual &&
+       ! test -s actual &&
+       git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config -f .gitmodules submodule.subname.ignore all &&
+       git diff HEAD^..HEAD >actual &&
+       ! test -s actual &&
+       git config submodule.subname.ignore dirty &&
+       git config submodule.subname.path sub &&
+       git diff  HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       rm .gitmodules
+'
+
 test_expect_success 'git diff (empty submodule dir)' '
        : >empty &&
        rm -rf sub/* sub/.git &&
index ead204e5cb77804ee1b54cc1b2f31d35312fa758..2e513569477bb5efc389a2088a4d896620e50b8f 100755 (executable)
@@ -100,13 +100,11 @@ test_expect_success 'oneline' '
 
 test_expect_success 'diff-filter=A' '
 
-       actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
-       expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
-       test "$actual" = "$expect" || {
-               echo Oops
-               echo "Actual: $actual"
-               false
-       }
+       git log --pretty="format:%s" --diff-filter=A HEAD > actual &&
+       git log --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+       printf "fifth\nfourth\nthird\ninitial" > expect &&
+       test_cmp expect actual &&
+       test_cmp expect actual-separate
 
 '
 
@@ -203,6 +201,13 @@ test_expect_success 'log --grep' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --grep option parsing' '
+       echo second >expect &&
+       git log -1 --pretty="tformat:%s" --grep sec >actual &&
+       test_cmp expect actual &&
+       test_must_fail git log -1 --pretty="tformat:%s" --grep
+'
+
 test_expect_success 'log -i --grep' '
        echo Second >expect &&
        git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
index 319e389ed0dbb9c920d8d619cdf94ab52145e9f4..0b489f5b1227268c050c1256d105d57d871f5698 100755 (executable)
@@ -4,6 +4,11 @@ test_description='pulling into void'
 
 . ./test-lib.sh
 
+modify () {
+       sed -e "$1" <"$2" >"$2.x" &&
+       mv "$2.x" "$2"
+}
+
 D=`pwd`
 
 test_expect_success setup '
@@ -160,4 +165,61 @@ test_expect_success 'pull --rebase works on branch yet to be born' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup for detecting upstreamed changes' '
+       mkdir src &&
+       (cd src &&
+        git init &&
+        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" > stuff &&
+        git add stuff &&
+        git commit -m "Initial revision"
+       ) &&
+       git clone src dst &&
+       (cd src &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "5->43" &&
+        modify s/6/42/ stuff &&
+        git commit -a -m "Make it bigger"
+       ) &&
+       (cd dst &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "Independent discovery of 5->43"
+       )
+'
+
+test_expect_success 'git pull --rebase detects upstreamed changes' '
+       (cd dst &&
+        git pull --rebase &&
+        test -z "$(git ls-files -u)"
+       )
+'
+
+test_expect_success 'setup for avoiding reapplying old patches' '
+       (cd dst &&
+        test_might_fail git rebase --abort &&
+        git reset --hard origin/master
+       ) &&
+       git clone --bare src src-replace.git &&
+       rm -rf src &&
+       mv src-replace.git src &&
+       (cd dst &&
+        modify s/2/22/ stuff &&
+        git commit -a -m "Change 2" &&
+        modify s/3/33/ stuff &&
+        git commit -a -m "Change 3" &&
+        modify s/4/44/ stuff &&
+        git commit -a -m "Change 4" &&
+        git push &&
+
+        modify s/44/55/ stuff &&
+        git commit --amend -a -m "Modified Change 4"
+       )
+'
+
+test_expect_success 'git pull --rebase does not reapply old patches' '
+       (cd dst &&
+        test_must_fail git pull --rebase &&
+        test 1 = $(find .git/rebase-apply -name "000*" | wc -l)
+       )
+'
+
 test_done
index 044603c26ed62e3ddf03ebb2542f783e4dd7d9ff..6b2a5f4a65659b7fcfcd9d096a0b43473e1cf1d6 100755 (executable)
@@ -60,6 +60,15 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
        grep "bad tree object" output.err
 '
 
+test_expect_success 'upload-pack error message when bad ref requested' '
+
+       printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+               "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+       test_must_fail git upload-pack . <input >output 2>output.err &&
+       grep -q "not our ref" output.err &&
+       ! grep -q multi_ack_detailed output.err
+'
+
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
 
        printf "0032want %s\n00000009done\n0000" \
index 58428d9f5c3d079b0e842b5851b100f9e895a239..fb8291c812854608777dd8d35edfcda78df5069a 100755 (executable)
@@ -123,6 +123,12 @@ test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
 
 '
 
+test_expect_success 'rev-list --glob refs/heads/subspace/*' '
+
+       compare rev-list "subspace/one subspace/two" "--glob refs/heads/subspace/*"
+
+'
+
 test_expect_success 'rev-list --glob=heads/subspace/*' '
 
        compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
index 8ab3d61f445725ec48b8b4ce28cfbf4a9ce13844..2cf42c73f14ef5069d096fb29e67dac571227cfe 100755 (executable)
@@ -58,7 +58,7 @@ test_expect_success 'pull with -X' '
        git reset --hard master && git pull -s recursive -X ours . side &&
        git reset --hard master && git pull -s recursive -Xtheirs . side &&
        git reset --hard master && git pull -s recursive -X theirs . side &&
-       git reset --hard master && ! git pull -s recursive -X bork . side
+       git reset --hard master && test_must_fail git pull -s recursive -X bork . side
 '
 
 test_done
index 7d8ed68befed0e85ad85f9f933a2b887125e38e8..3a43571cab78bce546ca714c8d53b76fc94c6ee2 100755 (executable)
@@ -438,4 +438,20 @@ test_expect_success 'force removal of nested git work tree' '
        ! test -d bar
 '
 
+test_expect_success 'git clean -e' '
+       rm -fr repo &&
+       mkdir repo &&
+       (
+               cd repo &&
+               git init &&
+               touch 1 2 3 known &&
+               git add known &&
+               git clean -f -e 1 -e 2 &&
+               test -e 1 &&
+               test -e 2 &&
+               ! (test -e 3) &&
+               test -e known
+       )
+'
+
 test_done
index 4a7b8933f4bf1036ba1e044d6e2dc54d72084039..6ec559db0fd2c1a7ef560543816ac2b5cf784809 100755 (executable)
@@ -54,13 +54,129 @@ test_expect_success setup '
        git merge -s ours a
 '
 
-test_expect_success 'merging with modify/modify conflict' '
+# History setup
+#
+#      b
+#    /   \
+#   a     d
+#    \   /
+#      c
+#
+# a in the main repository records to sub-a in the submodule and
+# analogous b and c. d should be automatically found by merging c into
+# b in the main repository.
+test_expect_success 'setup for merge search' '
+       mkdir merge-search &&
+       cd merge-search &&
+       git init &&
+       mkdir sub &&
+       (cd sub &&
+        git init &&
+        echo "file-a" > file-a &&
+        git add file-a &&
+        git commit -m "sub-a" &&
+        git branch sub-a) &&
+       git add sub &&
+       git commit -m "a" &&
+       git branch a &&
+
+       git checkout -b b &&
+       (cd sub &&
+        git checkout -b sub-b &&
+        echo "file-b" > file-b &&
+        git add file-b &&
+        git commit -m "sub-b") &&
+       git commit -a -m "b" &&
+
+       git checkout -b c a &&
+       (cd sub &&
+        git checkout -b sub-c sub-a &&
+        echo "file-c" > file-c &&
+        git add file-c &&
+        git commit -m "sub-c") &&
+       git commit -a -m "c" &&
+
+       git checkout -b d a &&
+       (cd sub &&
+        git checkout -b sub-d sub-b &&
+        git merge sub-c) &&
+       git commit -a -m "d" &&
+       git branch test b &&
+       cd ..
+'
+
+test_expect_success 'merge with one side as a fast-forward of the other' '
+       (cd merge-search &&
+        git checkout -b test-forward b &&
+        git merge d &&
+        git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_cmp actual expect)
+'
+
+test_expect_success 'merging should conflict for non fast-forward' '
+       (cd merge-search &&
+        git checkout -b test-nonforward b &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_must_fail git merge c 2> actual  &&
+        grep $(cat expect) actual > /dev/null &&
+        git reset --hard)
+'
+
+test_expect_success 'merging should fail for ambiguous common parent' '
+       cd merge-search &&
+       git checkout -b test-ambiguous b &&
+       (cd sub &&
+        git checkout -b ambiguous sub-b &&
+        git merge sub-c &&
+        git rev-parse sub-d > ../expect1 &&
+        git rev-parse ambiguous > ../expect2) &&
+       test_must_fail git merge c 2> actual &&
+       grep $(cat expect1) actual > /dev/null &&
+       grep $(cat expect2) actual > /dev/null &&
+       git reset --hard &&
+       cd ..
+'
+
+# in a situation like this
+#
+# submodule tree:
+#
+#    sub-a --- sub-b --- sub-d
+#
+# main tree:
+#
+#    e (sub-a)
+#   /
+#  bb (sub-b)
+#   \
+#    f (sub-d)
+#
+# A merge between e and f should fail because one of the submodule
+# commits (sub-a) does not descend from the submodule merge-base (sub-b).
+#
+test_expect_success 'merging should fail for changes that are backwards' '
+       cd merge-search &&
+       git checkout -b bb a &&
+       (cd sub &&
+        git checkout sub-b) &&
+       git commit -a -m "bb" &&
+
+       git checkout -b e bb &&
+       (cd sub &&
+        git checkout sub-a) &&
+       git commit -a -m "e" &&
+
+       git checkout -b f bb &&
+       (cd sub &&
+        git checkout sub-d) &&
+       git commit -a -m "f" &&
 
-       git checkout -b test1 a &&
-       test_must_fail git merge b &&
-       test -f .git/MERGE_MSG &&
-       git diff &&
-       test -n "$(git ls-files -u)"
+       git checkout -b test-backward e &&
+       test_must_fail git merge f &&
+       cd ..
 '
 
 test_expect_success 'merging with a modify/modify conflict between merge bases' '
index a72fe3ae640378350102a07124aee201fb1c637b..9c14b853c07f26722953c8a1f778a41af8808cf1 100755 (executable)
@@ -808,24 +808,38 @@ test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
        (exit $status)
 '
 
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar' && cd .. && git add sm)
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
+
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #      modified:   dir1/modified
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
@@ -834,19 +848,89 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+       git config diff.ignoreSubmodules dirty &&
+       git status >output &&
+       test_cmp expect output &&
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname
+'
+
 test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+       git config diff.ignoreSubmodules dirty &&
+       git status >output &&
+       ! test -s actual &&
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
        echo modified > sm/foo &&
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
@@ -855,16 +939,21 @@ cat > expect << EOF
 #      modified:   dir1/modified
 #      modified:   sm (modified content)
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
@@ -872,10 +961,34 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodules w
        test_cmp expect output
 '
 
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
 
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
@@ -883,21 +996,26 @@ cat > expect << EOF
 #      modified:   dir1/modified
 #      modified:   sm (new commits)
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Submodules changed but not updated:
 #
-# * sm $head...$head2 (1):
+# * sm $new_head...$head2 (1):
 #   > 2nd commit
 #
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
@@ -905,10 +1023,47 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodule su
        test_cmp expect output
 '
 
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
 
 cat > expect << EOF
 # On branch master
@@ -921,6 +1076,7 @@ cat > expect << EOF
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
@@ -935,4 +1091,23 @@ test_expect_success "--ignore-submodules=all suppresses submodule summary" '
        test_cmp expect output
 '
 
+test_expect_failure '.gitmodules ignore=all suppresses submodule summary' '
+       git config --add -f .gitmodules submodule.subname.ignore all &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_failure '.git/config ignore=all suppresses submodule summary' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore all &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_done
index 3ea33db6c73df763c22ccfab9f0cdcb2bbaef4d7..643ab03f992be8339b169b3bef0922ff3cdb04b9 100755 (executable)
@@ -111,7 +111,7 @@ test_expect_success '--amend option with empty author' '
        test_when_finished "git checkout Initial" &&
        echo "Empty author test" >>foo &&
        test_tick &&
-       ! git commit -a -m "empty author" --amend 2>err &&
+       test_must_fail git commit -a -m "empty author" --amend 2>err &&
        grep "empty ident" err
 '
 
@@ -125,7 +125,7 @@ test_expect_success '--amend option with missing author' '
        test_when_finished "git checkout Initial" &&
        echo "Missing author test" >>foo &&
        test_tick &&
-       ! git commit -a -m "malformed author" --amend 2>err &&
+       test_must_fail git commit -a -m "malformed author" --amend 2>err &&
        grep "empty ident" err
 '
 
index 49f4e1599acd829fdd930c1f9b5bc30ac719053d..d82349a6a8d6c12c3ca4398a18bd6c1dd42efdc9 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'setup' '
 test_expect_success 'will not overwrite untracked file' '
        git reset --hard c1 &&
        cat important > c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        test_cmp important c2.c
 '
 
@@ -39,7 +39,7 @@ test_expect_success 'will not overwrite new file' '
        git reset --hard c1 &&
        cat important > c2.c &&
        git add c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        test_cmp important c2.c
 '
 
@@ -48,7 +48,7 @@ test_expect_success 'will not overwrite staged changes' '
        cat important > c2.c &&
        git add c2.c &&
        rm c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        git checkout c2.c &&
        test_cmp important c2.c
 '
@@ -58,7 +58,7 @@ test_expect_success 'will not overwrite removed file' '
        git rm c1.c &&
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        test_cmp important c1.c
 '
 
@@ -68,7 +68,7 @@ test_expect_success 'will not overwrite re-added file' '
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
        git add c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        test_cmp important c1.c
 '
 
@@ -79,7 +79,7 @@ test_expect_success 'will not overwrite removed file with staged changes' '
        cat important > c1.c &&
        git add c1.c &&
        rm c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        git checkout c1.c &&
        test_cmp important c1.c
 '
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
new file mode 100755 (executable)
index 0000000..1a109b4
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+test_description='unpack-trees error messages'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+       echo one >one &&
+       git add one &&
+       git commit -a -m First &&
+
+       git checkout -b branch &&
+       echo two >two &&
+       echo three >three &&
+       echo four >four &&
+       echo five >five &&
+       git add two three four five &&
+       git commit -m Second &&
+
+       git checkout master &&
+       echo other >two &&
+       echo other >three &&
+       echo other >four &&
+       echo other >five
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+       two
+       three
+       four
+       five
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files overwritten by merge' '
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by merge:
+       two
+       three
+       four
+Please, commit your changes or stash them before you can merge.
+error: The following untracked working tree files would be overwritten by merge:
+       five
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files or local changes ovewritten by merge' '
+       git add two &&
+       git add three &&
+       git add four &&
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/two
+       rep/one
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'cannot switch branches because of local changes' '
+       git add five &&
+       mkdir rep &&
+       echo one >rep/one &&
+       echo two >rep/two &&
+       git add rep/one rep/two &&
+       git commit -m Fourth &&
+       git checkout master &&
+       echo uno >rep/one &&
+       echo dos >rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/two
+       rep/one
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'not uptodate file porcelain checkout error' '
+       git add rep/one rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Updating the following directories would lose untracked files in it:
+       rep2
+       rep
+
+EOF
+
+test_expect_success 'not_uptodate_dir porcelain checkout error' '
+       git init uptodate &&
+       cd uptodate &&
+       mkdir rep &&
+       mkdir rep2 &&
+       touch rep/foo &&
+       touch rep2/foo &&
+       git add rep/foo rep2/foo &&
+       git commit -m init &&
+       git checkout -b branch &&
+       git rm rep -r &&
+       git rm rep2 -r &&
+       >rep &&
+       >rep2 &&
+       git add rep rep2&&
+       git commit -m "added test as a file" &&
+       git checkout master &&
+       >rep/untracked-file &&
+       >rep2/untracked-file &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out ../expect
+'
+
+test_done
index e768c3eb2d48a9af2fc92729a6c20126f67ec866..3bd74042ef46ee8b4e4477d1c23ba0914963de33 100755 (executable)
@@ -14,6 +14,7 @@ Testing basic merge tool invocation'
 # running mergetool
 
 test_expect_success 'setup' '
+    git config rerere.enabled true &&
     echo master >file1 &&
     mkdir subdir &&
     echo master sub >subdir/file3 &&
@@ -67,23 +68,47 @@ test_expect_success 'mergetool crlf' '
 '
 
 test_expect_success 'mergetool in subdir' '
-    git checkout -b test3 branch1
-    cd subdir && (
-    test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
-    test "$(cat file3)" = "master new sub" )
+    git checkout -b test3 branch1 &&
+    (
+       cd subdir &&
+       test_must_fail git merge master >/dev/null 2>&1 &&
+       ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+       test "$(cat file3)" = "master new sub"
+    )
 '
 
-# We can't merge files from parent directories when running mergetool
-# from a subdir. Is this a bug?
-#
-#test_expect_failure 'mergetool in subdir' '
-#    cd subdir && (
-#    ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-#    ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-#    test "$(cat ../file1)" = "master updated" &&
-#    test "$(cat ../file2)" = "master new" &&
-#    git commit -m "branch1 resolved with mergetool - subdir" )
-#'
+test_expect_success 'mergetool on file in parent dir' '
+    (
+       cd subdir &&
+       ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+       ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       git commit -m "branch1 resolved with mergetool - subdir"
+    )
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+    git checkout -b test4 branch1 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+    (
+       cd subdir &&
+       git config rerere.enabled false &&
+       test_must_fail git merge master &&
+       git mergetool --no-prompt &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       test "$(cat file3)" = "master new sub" &&
+       git add ../file1 ../file2 file3 &&
+       git commit -m "branch2 resolved by mergetool from subdir"
+    )
+'
 
 test_done
index 8a6322765c965bfc3a9158cc14312fdf03295090..023f225a4b1f22c66fb291a76690564696d56035 100755 (executable)
@@ -65,7 +65,7 @@ do
 
        test_expect_success "grep -w $L (w)" '
                : >expected &&
-               ! git grep -n -w -e "^w" >actual &&
+               test_must_fail git grep -n -w -e "^w" >actual &&
                test_cmp expected actual
        '
 
index 13766ab160e48d2b2ecb6e95079077e4a0704c2c..d5adae640b0b92e049de3c5d5e8996e1db06bcc4 100755 (executable)
@@ -271,6 +271,17 @@ test_expect_success 'able to dcommit to a subdirectory' "
        test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
        "
 
+test_expect_success 'dcommit should not fail with a touched file' '
+       test_commit "commit-new-file-foo2" foo2 &&
+       test-chmtime =-60 foo &&
+       git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+       test-chmtime =-60 foo &&
+       git svn rebase
+'
+
 test_expect_success 'able to set-tree to a subdirectory' "
        echo cba > d &&
        git update-index d &&
index 134411e0a56142930a418ca15bd0902837c7bdc1..3c4f31925fe0498717b56ff95cf42a9a7450a236 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success 'setup svnrepo' '
        '
 
 test_expect_success 'start import with incomplete authors file' '
-       ! git svn clone --authors-file=svn-authors "$svnrepo" x
+       test_must_fail git svn clone --authors-file=svn-authors "$svnrepo" x
        '
 
 test_expect_success 'imported 2 revisions successfully' '
@@ -63,7 +63,7 @@ test_expect_success 'authors-file against globs' '
        '
 
 test_expect_success 'fetch fails on ee' '
-       ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+       ( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
        '
 
 tmp_config_get () {
index f337959cccc78c40094ed6d81bdd502860e5e02b..22d80b0be2b94515132a79401b719f98794f4616 100755 (executable)
@@ -39,7 +39,7 @@ do
        (
                cd $H &&
                git config --unset i18n.commitencoding &&
-               ! git svn dcommit
+               test_must_fail git svn dcommit
        )
        '
 done
index 0735526d4ba4f2b692e000bcd4116be91d650355..e8559046296ce5570ec46b2b3a9280f0a493bbc4 100755 (executable)
@@ -41,7 +41,7 @@ test_expect_success 'modify hidden file in SVN repo' '
 test_expect_success 'fetch fails on modified hidden file' '
        ( cd g &&
          git svn find-rev refs/remotes/git-svn > ../expect &&
-         ! git svn fetch 2> ../errors &&
+         test_must_fail git svn fetch 2> ../errors &&
          git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
        fgrep "not found in commit" errors &&
        test_cmp expect expect2
diff --git a/t/t9155-git-svn-fetch-deleted-tag.sh b/t/t9155-git-svn-fetch-deleted-tag.sh
new file mode 100755 (executable)
index 0000000..a486a98
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+       mkdir -p import/trunk/subdir &&
+       mkdir -p import/branches &&
+       mkdir -p import/tags &&
+       echo "base" >import/trunk/subdir/file &&
+       svn_cmd import -m "import for git svn" import "$svnrepo" &&
+       rm -rf import &&
+
+       svn_cmd mkdir -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+       svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+       svn_cmd co "$svnrepo/trunk" svn_project &&
+       (cd svn_project &&
+               echo "trunk change" >>subdir/file &&
+               svn_cmd ci -m "trunk change" subdir/file &&
+
+               svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+               echo "branch change" >>subdir/file &&
+               svn_cmd ci -m "branch change" subdir/file
+       ) &&
+
+       svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+       git svn init --stdlayout "$svnrepo" git_project &&
+       cd git_project &&
+       git svn fetch &&
+
+       git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
+       git diff --exit-code master:subdir/file tags/mytag^:file
+'
+
+test_done
diff --git a/t/t9156-git-svn-fetch-deleted-tag-2.sh b/t/t9156-git-svn-fetch-deleted-tag-2.sh
new file mode 100755 (executable)
index 0000000..5ce7e2f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+       mkdir -p import/branches &&
+       mkdir -p import/tags &&
+       mkdir -p import/trunk/subdir1 &&
+       mkdir -p import/trunk/subdir2 &&
+       mkdir -p import/trunk/subdir3 &&
+       echo "file1" >import/trunk/subdir1/file &&
+       echo "file2" >import/trunk/subdir2/file &&
+       echo "file3" >import/trunk/subdir3/file &&
+       svn_cmd import -m "import for git svn" import "$svnrepo" &&
+       rm -rf import &&
+
+       svn_cmd co "$svnrepo/trunk" svn_project &&
+       (cd svn_project &&
+               echo "change1" >>subdir1/file &&
+               echo "change2" >>subdir2/file &&
+               echo "change3" >>subdir3/file &&
+               svn_cmd ci -m "change" .
+       ) &&
+
+       svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+       git svn init --stdlayout "$svnrepo" git_project &&
+       cd git_project &&
+       git svn fetch &&
+
+       git diff --exit-code master:subdir3/file tags/mytag:file &&
+       git diff --exit-code master:subdir2/file tags/mytag^:file &&
+       git diff --exit-code master:subdir1/file tags/mytag^^:file
+'
+
+test_done
index 2aeed7bd0601a0587f99250377001be6287772e5..96d07f183377f8822257cb3a1dad29023b391462 100755 (executable)
@@ -853,6 +853,60 @@ test_expect_success \
        'git fast-import <input &&
         test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
 
+test_expect_success \
+       'N: copy directory by id' \
+       'cat >expect <<-\EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $subdir file3
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+        compare_diff_raw expect actual'
+
+test_expect_success \
+       'N: modify copied tree' \
+       'cat >expect <<-\EOF &&
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      file3/file5
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $subdir file3
+
+       commit refs/heads/N5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       modify directory copy
+       COMMIT
+
+       M 644 inline file3/file5
+       data <<EOF
+       $file5_data
+       EOF
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+        compare_diff_raw expect actual'
+
 ###
 ### series O
 ###
index 29fd7209cf1aff851f0f1f95ccecd0d949b3da6c..3a3d4c4723d4dece710f4f959ab689e1f8fb8760 100644 (file)
@@ -545,6 +545,38 @@ test_external_without_stderr () {
        fi
 }
 
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+       if ! [ -f "$1" ]
+       then
+               echo "File $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_dir () {
+       if ! [ -d "$1" ]
+       then
+               echo "Directory $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_missing () {
+       if [ -e "$1" ]
+       then
+               echo "Path exists:"
+               ls -ld "$1"
+               if [ $# -ge 1 ]; then
+                       echo "$*"
+               fi
+               false
+       fi
+}
+
+
 # This is not among top-level (test_expect_success | test_expect_failure)
 # but is a prefix that can be used in the test script, like:
 #
index 191fbf798a5e5db1abeafc719e8ec0d986a1050c..acfc88e3f1f0e99863512bf2c6ea82f1283cc35d 100644 (file)
@@ -689,7 +689,7 @@ static int push_refs_with_export(struct transport *transport,
        struct child_process *helper, exporter;
        struct helper_data *data = transport->data;
        char *export_marks = NULL, *import_marks = NULL;
-       struct string_list revlist_args = { NULL, 0, 0 };
+       struct string_list revlist_args = STRING_LIST_INIT_NODUP;
        struct strbuf buf = STRBUF_INIT;
 
        helper = get_helper(transport);
index 67a9a0c5a5bf7d7125765679318cfcd68c160da7..a9bbf4e2354df5ce6b010873b419731343a12c7d 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "unpack-trees.h"
 #include "tree.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
@@ -310,6 +311,7 @@ static void free_extended_entry(struct tree_desc_x *t)
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
        int ret = 0;
+       int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
        int i;
        struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
@@ -377,8 +379,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                if (!mask)
                        break;
                ret = info->fn(n, mask, dirmask, entry, info);
-               if (ret < 0)
-                       break;
+               if (ret < 0) {
+                       error = ret;
+                       if (!info->show_all_errors)
+                               break;
+               }
                mask &= ret;
                ret = 0;
                for (i = 0; i < n; i++)
@@ -389,7 +394,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
        for (i = 0; i < n; i++)
                free_extended_entry(tx + i);
        free(tx);
-       return ret;
+       return error;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
index f78361a676be002a36bed0f243217c4e0ae9c8d1..7e3e0b5ad16710c06464726ac04d2b1c48af3708 100644 (file)
@@ -48,6 +48,7 @@ struct traverse_info {
        unsigned long conflicts;
        traverse_callback_t fn;
        void *data;
+       int show_all_errors;
 };
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
index f561d88156b827b15bad7dd4e00f944718149bd9..3c7a7c9cde745c1b438f60aceea7e556dded81d7 100644 (file)
  * Error messages expected by scripts out of plumbing commands such as
  * read-tree.  Non-scripted Porcelain is not required to use these messages
  * and in fact are encouraged to reword them to better suit their particular
- * situation better.  See how "git checkout" replaces not_uptodate_file to
- * explain why it does not allow switching between branches when you have
- * local changes, for example.
+ * situation better.  See how "git checkout" and "git merge" replaces
+ * them using set_porcelain_error_msgs(), for example.
  */
-static struct unpack_trees_error_msgs unpack_plumbing_errors = {
-       /* would_overwrite */
+const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+       /* ERROR_WOULD_OVERWRITE */
        "Entry '%s' would be overwritten by merge. Cannot merge.",
 
-       /* not_uptodate_file */
+       /* ERROR_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot merge.",
 
-       /* not_uptodate_dir */
+       /* ERROR_NOT_UPTODATE_DIR */
        "Updating '%s' would lose untracked files in it",
 
-       /* would_lose_untracked */
-       "Untracked working tree file '%s' would be %s by merge.",
+       /* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
+       "Untracked working tree file '%s' would be overwritten by merge.",
 
-       /* bind_overlap */
+       /* ERROR_WOULD_LOSE_UNTRACKED_REMOVED */
+       "Untracked working tree file '%s' would be removed by merge.",
+
+       /* ERROR_BIND_OVERLAP */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
 
-       /* sparse_not_uptodate_file */
+       /* ERROR_SPARSE_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot update sparse checkout.",
 
-       /* would_lose_orphaned */
-       "Working tree file '%s' would be %s by sparse checkout update.",
+       /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
+       "Working tree file '%s' would be overwritten by sparse checkout update.",
+
+       /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
+       "Working tree file '%s' would be removed by sparse checkout update.",
 };
 
-#define ERRORMSG(o,fld) \
-       ( ((o) && (o)->msgs.fld) \
-       ? ((o)->msgs.fld) \
-       : (unpack_plumbing_errors.fld) )
+#define ERRORMSG(o,type) \
+       ( ((o) && (o)->msgs[(type)]) \
+         ? ((o)->msgs[(type)])      \
+         : (unpack_plumbing_errors[(type)]) )
 
 static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        unsigned int set, unsigned int clear)
@@ -53,12 +58,82 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 
        clear |= CE_HASHED | CE_UNHASHED;
 
+       if (set & CE_REMOVE)
+               set |= CE_WT_REMOVE;
+
        memcpy(new, ce, size);
        new->next = NULL;
        new->ce_flags = (new->ce_flags & ~clear) | set;
        add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+/*
+ * add error messages on path <path>
+ * corresponding to the type <e> with the message <msg>
+ * indicating if it should be display in porcelain or not
+ */
+static int add_rejected_path(struct unpack_trees_options *o,
+                            enum unpack_trees_error_types e,
+                            const char *path)
+{
+       struct rejected_paths_list *newentry;
+       int porcelain = o && (o)->msgs[e];
+       /*
+        * simply display the given error message if in plumbing mode
+        */
+       if (!porcelain)
+               o->show_all_errors = 0;
+       if (!o->show_all_errors)
+               return error(ERRORMSG(o, e), path);
+
+       /*
+        * Otherwise, insert in a list for future display by
+        * display_error_msgs()
+        */
+       newentry = xmalloc(sizeof(struct rejected_paths_list));
+       newentry->path = (char *)path;
+       newentry->next = o->unpack_rejects[e];
+       o->unpack_rejects[e] = newentry;
+       return -1;
+}
+
+/*
+ * free all the structures allocated for the error <e>
+ */
+static void free_rejected_paths(struct unpack_trees_options *o,
+                               enum unpack_trees_error_types e)
+{
+       while (o->unpack_rejects[e]) {
+               struct rejected_paths_list *del = o->unpack_rejects[e];
+               o->unpack_rejects[e] = o->unpack_rejects[e]->next;
+               free(del);
+       }
+       free(o->unpack_rejects[e]);
+}
+
+/*
+ * display all the error messages stored in a nice way
+ */
+static void display_error_msgs(struct unpack_trees_options *o)
+{
+       int e;
+       int something_displayed = 0;
+       for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
+               if (o->unpack_rejects[e]) {
+                       struct rejected_paths_list *rp;
+                       struct strbuf path = STRBUF_INIT;
+                       something_displayed = 1;
+                       for (rp = o->unpack_rejects[e]; rp; rp = rp->next)
+                               strbuf_addf(&path, "\t%s\n", rp->path);
+                       error(ERRORMSG(o, e), path.buf);
+                       strbuf_release(&path);
+                       free_rejected_paths(o, e);
+               }
+       }
+       if (something_displayed)
+               printf("Aborting\n");
+}
+
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
@@ -84,7 +159,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
-                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
+                       if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
 
@@ -104,12 +179,6 @@ static int check_updates(struct unpack_trees_options *o)
                                unlink_entry(ce);
                        continue;
                }
-
-               if (ce->ce_flags & CE_REMOVE) {
-                       display_progress(progress, ++cnt);
-                       if (o->update)
-                               unlink_entry(ce);
-               }
        }
        remove_marked_cache_entries(&o->result);
        remove_scheduled_dirs();
@@ -132,15 +201,12 @@ static int check_updates(struct unpack_trees_options *o)
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);
+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
 
 static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
 {
        const char *basename;
 
-       if (ce_stage(ce))
-               return 0;
-
        basename = strrchr(ce->name, '/');
        basename = basename ? basename+1 : ce->name;
        return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
@@ -150,19 +216,36 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 {
        int was_skip_worktree = ce_skip_worktree(ce);
 
-       if (will_have_skip_worktree(ce, o))
+       if (!ce_stage(ce) && will_have_skip_worktree(ce, o))
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
        /*
-        * We only care about files getting into the checkout area
-        * If merge strategies want to remove some, go ahead, this
-        * flag will be removed eventually in unpack_trees() if it's
-        * outside checkout area.
+        * if (!was_skip_worktree && !ce_skip_worktree()) {
+        *      This is perfectly normal. Move on;
+        * }
         */
-       if (ce->ce_flags & CE_REMOVE)
-               return 0;
+
+       /*
+        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+        * area as a result of ce_skip_worktree() shortcuts in
+        * verify_absent() and verify_uptodate().
+        * Make sure they don't modify worktree if they are already
+        * outside checkout area
+        */
+       if (was_skip_worktree && ce_skip_worktree(ce)) {
+               ce->ce_flags &= ~CE_UPDATE;
+
+               /*
+                * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
+                * on to get that file removed from both index and worktree.
+                * If that file is already outside worktree area, don't
+                * bother remove it.
+                */
+               if (ce->ce_flags & CE_REMOVE)
+                       ce->ce_flags &= ~CE_WT_REMOVE;
+       }
 
        if (!was_skip_worktree && ce_skip_worktree(ce)) {
                /*
@@ -175,7 +258,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
                ce->ce_flags |= CE_WT_REMOVE;
        }
        if (was_skip_worktree && !ce_skip_worktree(ce)) {
-               if (verify_absent_sparse(ce, "overwritten", o))
+               if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
                ce->ce_flags |= CE_UPDATE;
        }
@@ -755,6 +838,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                setup_traverse_info(&info, prefix);
                info.fn = unpack_callback;
                info.data = o;
+               info.show_all_errors = o->show_all_errors;
 
                if (o->prefix) {
                        /*
@@ -803,14 +887,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                                ret = -1;
                                goto done;
                        }
-                       /*
-                        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
-                        * area as a result of ce_skip_worktree() shortcuts in
-                        * verify_absent() and verify_uptodate(). Clear them.
-                        */
-                       if (ce_skip_worktree(ce))
-                               ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
-                       else
+                       if (!ce_skip_worktree(ce))
                                empty_worktree = 0;
 
                }
@@ -834,6 +911,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        return ret;
 
 return_failed:
+       if (o->show_all_errors)
+               display_error_msgs(o);
        mark_all_ce_unused(o->src_index);
        ret = unpack_failed(o, NULL);
        goto done;
@@ -843,7 +922,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
 static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       return error(ERRORMSG(o, would_overwrite), ce->name);
+       return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -865,7 +944,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  */
 static int verify_uptodate_1(struct cache_entry *ce,
                                   struct unpack_trees_options *o,
-                                  const char *error_msg)
+                                  enum unpack_trees_error_types error_type)
 {
        struct stat st;
 
@@ -890,7 +969,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
        if (errno == ENOENT)
                return 0;
        return o->gently ? -1 :
-               error(error_msg, ce->name);
+               add_rejected_path(o, error_type, ce->name);
 }
 
 static int verify_uptodate(struct cache_entry *ce,
@@ -898,13 +977,13 @@ static int verify_uptodate(struct cache_entry *ce,
 {
        if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
                return 0;
-       return verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce,
                                  struct unpack_trees_options *o)
 {
-       return verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -920,13 +999,15 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+static int verify_clean_submodule(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
+static int verify_clean_subdirectory(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        /*
@@ -947,7 +1028,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                 */
                if (!hashcmp(sha1, ce->sha1))
                        return 0;
-               return verify_clean_submodule(ce, action, o);
+               return verify_clean_submodule(ce, error_type, o);
        }
 
        /*
@@ -991,7 +1072,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        i = read_directory(&d, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, not_uptodate_dir), ce->name);
+                       add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
        free(pathbuf);
        return cnt;
 }
@@ -1016,9 +1097,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent_1(struct cache_entry *ce, const char *action,
-                                struct unpack_trees_options *o,
-                                const char *error_msg)
+static int verify_absent_1(struct cache_entry *ce,
+                                enum unpack_trees_error_types error_type,
+                                struct unpack_trees_options *o)
 {
        struct stat st;
 
@@ -1056,7 +1137,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                         * files that are in "foo/" we would lose
                         * them.
                         */
-                       if (verify_clean_subdirectory(ce, action, o) < 0)
+                       if (verify_clean_subdirectory(ce, error_type, o) < 0)
                                return -1;
                        return 0;
                }
@@ -1073,22 +1154,28 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                }
 
                return o->gently ? -1 :
-                       error(ERRORMSG(o, would_lose_untracked), ce->name, action);
+                       add_rejected_path(o, error_type, ce->name);
        }
        return 0;
 }
-static int verify_absent(struct cache_entry *ce, const char *action,
+static int verify_absent(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
        if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
                return 0;
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));
+       return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce, const char *action,
+static int verify_absent_sparse(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));
+       enum unpack_trees_error_types orphaned_error = error_type;
+       if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
+               orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
+
+       return verify_absent_1(ce, orphaned_error, o);
 }
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
@@ -1097,8 +1184,10 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
        int update = CE_UPDATE;
 
        if (!old) {
-               if (verify_absent(merge, "overwritten", o))
+               if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
+               if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
+                       update |= CE_SKIP_WORKTREE;
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
@@ -1135,7 +1224,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 {
        /* Did it exist in the index? */
        if (!old) {
-               if (verify_absent(ce, "removed", o))
+               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                        return -1;
                return 0;
        }
@@ -1284,7 +1373,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
                        if (index)
                                return deleted_entry(index, index, o);
                        if (ce && !head_deleted) {
-                               if (verify_absent(ce, "removed", o))
+                               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                                        return -1;
                        }
                        return 0;
@@ -1417,7 +1506,7 @@ int bind_merge(struct cache_entry **src,
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, bind_overlap), a->name, old->name);
+                       error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
        if (!a)
                return keep_entry(old, o);
        else
index ef70eab39025fcdaccda059692ae447a13fa0aeb..6e049b09977cf89eca87f5ef22e645dd2027e414 100644 (file)
@@ -9,14 +9,22 @@ struct exclude_list;
 typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
 
-struct unpack_trees_error_msgs {
-       const char *would_overwrite;
-       const char *not_uptodate_file;
-       const char *not_uptodate_dir;
-       const char *would_lose_untracked;
-       const char *bind_overlap;
-       const char *sparse_not_uptodate_file;
-       const char *would_lose_orphaned;
+enum unpack_trees_error_types {
+       ERROR_WOULD_OVERWRITE = 0,
+       ERROR_NOT_UPTODATE_FILE,
+       ERROR_NOT_UPTODATE_DIR,
+       ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
+       ERROR_BIND_OVERLAP,
+       ERROR_SPARSE_NOT_UPTODATE_FILE,
+       ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_ORPHANED_REMOVED,
+       NB_UNPACK_TREES_ERROR_TYPES
+};
+
+struct rejected_paths_list {
+       char *path;
+       struct rejected_paths_list *next;
 };
 
 struct unpack_trees_options {
@@ -33,12 +41,18 @@ struct unpack_trees_options {
                     diff_index_cached,
                     debug_unpack,
                     skip_sparse_checkout,
-                    gently;
+                    gently,
+                    show_all_errors;
        const char *prefix;
        int cache_bottom;
        struct dir_struct *dir;
        merge_fn_t fn;
-       struct unpack_trees_error_msgs msgs;
+       const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
+       /*
+        * Store error messages in an array, each case
+        * corresponding to a error message type
+        */
+       struct rejected_paths_list *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
 
        int head_idx;
        int merge_size;
index dc464d78b35659705ffb0cd233b80ab27e24e8bc..92f9530c6572a212c12ac917d774736286325e52 100644 (file)
@@ -105,7 +105,7 @@ static void show_edge(struct commit *commit)
        fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-static int do_rev_list(int in, int out, void *create_full_pack)
+static int do_rev_list(int in, int out, void *user_data)
 {
        int i;
        struct rev_info revs;
@@ -118,23 +118,18 @@ static int do_rev_list(int in, int out, void *create_full_pack)
        if (use_thin_pack)
                revs.edge_hint = 1;
 
-       if (create_full_pack) {
-               const char *args[] = {"rev-list", "--all", NULL};
-               setup_revisions(2, args, &revs, NULL);
-       } else {
-               for (i = 0; i < want_obj.nr; i++) {
-                       struct object *o = want_obj.objects[i].item;
-                       /* why??? */
-                       o->flags &= ~UNINTERESTING;
-                       add_pending_object(&revs, o, NULL);
-               }
-               for (i = 0; i < have_obj.nr; i++) {
-                       struct object *o = have_obj.objects[i].item;
-                       o->flags |= UNINTERESTING;
-                       add_pending_object(&revs, o, NULL);
-               }
-               setup_revisions(0, NULL, &revs, NULL);
+       for (i = 0; i < want_obj.nr; i++) {
+               struct object *o = want_obj.objects[i].item;
+               /* why??? */
+               o->flags &= ~UNINTERESTING;
+               add_pending_object(&revs, o, NULL);
+       }
+       for (i = 0; i < have_obj.nr; i++) {
+               struct object *o = have_obj.objects[i].item;
+               o->flags |= UNINTERESTING;
+               add_pending_object(&revs, o, NULL);
        }
+       setup_revisions(0, NULL, &revs, NULL);
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
@@ -487,7 +482,7 @@ static int get_common_commits(void)
 
 static void receive_needs(void)
 {
-       struct object_array shallows = {0, 0, NULL};
+       struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
 
@@ -554,7 +549,8 @@ static void receive_needs(void)
                 */
                o = lookup_object(sha1_buf);
                if (!o || !(o->flags & OUR_REF))
-                       die("git upload-pack: not our ref %s", line+5);
+                       die("git upload-pack: not our ref %s",
+                           sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
                        add_object_array(o, NULL, &want_obj);
index c49cc1b67e1164c561fb20839272bd8b1f7dde6d..e5522159b362811346b4306246391e960a4e7041 100644 (file)
@@ -82,6 +82,22 @@ PATTERNS("cpp",
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
         "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("csharp",
+        /* Keywords */
+        "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+        /* Methods and constructors */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+        /* Properties */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+        /* Type definitions */
+        "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+        /* Namespace */
+        "^[ \t]*(namespace[ \t]+.*)$",
+        /* -- */
+        "[a-zA-Z_][a-zA-Z0-9_]*"
+        "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+        "|[^[:space:]]|[\x80-\xff]+"),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
index 2f9e33c8fa172b129fd956abc4f74a5bf5543ba7..54b6b03b9cc4e168e6b6429679b61dc2a1d5197b 100644 (file)
@@ -313,8 +313,10 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
        if (!s->show_untracked_files)
                DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
-       if (s->ignore_submodule_arg)
+       if (s->ignore_submodule_arg) {
+               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+    }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
        rev.prune_data = s->pathspec;
@@ -331,8 +333,10 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
 
-       if (s->ignore_submodule_arg)
+       if (s->ignore_submodule_arg) {
+               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+       }
 
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_collect_updated_cb;