Merge branch 'ph/pull-rebase-detached'
authorJeff King <peff@peff.net>
Sun, 4 Nov 2012 13:00:06 +0000 (08:00 -0500)
committerJeff King <peff@peff.net>
Sun, 4 Nov 2012 13:00:06 +0000 (08:00 -0500)
Avoids spewing error messages when using "pull --rebase" on a
detached HEAD.

* ph/pull-rebase-detached:
git-pull: Avoid merge-base on detached head

68 files changed:
.gitignore
Documentation/diff-config.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-merge.txt
Documentation/git-push.txt
Documentation/git-remote-helpers.txt
Documentation/git-rm.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/gitmodules.txt
Documentation/merge-config.txt
Documentation/rev-list-options.txt
Documentation/technical/api-argv-array.txt
Makefile
argv-array.c
argv-array.h
attr.c
builtin/branch.c
builtin/commit.c
builtin/describe.c
builtin/grep.c
builtin/log.c
builtin/pack-objects.c
builtin/rm.c
builtin/show-ref.c
compat/mingw.c
compat/mingw.h
config.c
contrib/svn-fe/svn-fe.c
contrib/svn-fe/svnrdump_sim.py [new file with mode: 0755]
diff.c
git-filter-branch.sh
git-sh-setup.sh
git-submodule.sh
grep.c
grep.h
http.c
http.h
mergetools/p4merge
pager.c
refs.c
remote-curl.c
remote-testsvn.c [new file with mode: 0644]
revision.c
run-command.c
run-command.h
strbuf.c
submodule.c
submodule.h
t/t3200-branch.sh
t/t3600-rm.sh
t/t4055-diff-context.sh [new file with mode: 0755]
t/t4202-log.sh
t/t7003-filter-branch.sh
t/t7008-grep-binary.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t9020-remote-svn.sh [new file with mode: 0755]
t/test-lib.sh
test-svn-fe.c
transport-helper.c
upload-pack.c
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/svndump.c
vcs-svn/svndump.h
write_or_die.c
index a188a82bb1d6ab080c434a7096bd263545169f3c..f702415c12c5a4a66180f7ffd697347e5343ac4a 100644 (file)
 /git-remote-fd
 /git-remote-ext
 /git-remote-testgit
+/git-remote-testsvn
 /git-repack
 /git-replace
 /git-repo-config
index 67a90a828c04b7277fc603c6518fd0ecd8247d32..75ab8a51ca17ce825d2f1faee5de14abc2fdb6f6 100644 (file)
@@ -56,6 +56,10 @@ diff.statGraphWidth::
        Limit the width of the graph part in --stat output. If set, applies
        to all commands generating --stat output except format-patch.
 
+diff.context::
+       Generate diffs with <n> lines of context instead of the default of
+       3. This value is overridden by the -U option.
+
 diff.external::
        If this config variable is set, diff generation is not
        performed using the internal diff machinery, but using the
index 6d98ef3d2ab8c8e63e026dbd7e242d3c3bc3c79a..7fefdb0384307d0029eb6016bdf37735e84ebdd8 100644 (file)
@@ -196,9 +196,9 @@ objects from the source repository into a pack in the cloned repository.
        `--no-single-branch` is given to fetch the histories near the
        tips of all branches.
        Further fetches into the resulting repository will only update the
-       remote tracking branch for the branch this option was used for the
+       remote-tracking branch for the branch this option was used for the
        initial cloning.  If the HEAD at the remote did not point at any
-       branch when `--single-branch` clone was made, no remote tracking
+       branch when `--single-branch` clone was made, no remote-tracking
        branch is created.
 
 --recursive::
index 9594ac8e9de9645be71b1528dcb94705f1695283..3acf2e70d652906d696bb9b11e31d52786e1a4f9 100644 (file)
@@ -109,6 +109,10 @@ OPTIONS
        format. See linkgit:git-status[1] for details. Implies
        `--dry-run`.
 
+--long::
+       When doing a dry-run, give the output in a the long-format.
+       Implies `--dry-run`.
+
 -z::
 --null::
        When showing `short` or `porcelain` status output, terminate
index 20f9228511e82ac9ced81889bcc66f41504710a3..d34ea3c50bae192cf0f43c782662a48a3df18f8c 100644 (file)
@@ -99,7 +99,7 @@ commit or stash your changes before running 'git merge'.
        more than two parents (affectionately called an Octopus merge).
 +
 If no commit is given from the command line, and if `merge.defaultToUpstream`
-configuration variable is set, merge the remote tracking branches
+configuration variable is set, merge the remote-tracking branches
 that the current branch is configured to use as its upstream.
 See also the configuration section of this manual page.
 
index 22d2580129538898933d19582b8c292f6463f6a8..fe46c4258a9c8a9dbab0b789215716065bef00ad 100644 (file)
@@ -175,7 +175,7 @@ useful if you write an alias or script around 'git push'.
 
 --recurse-submodules=check|on-demand::
        Make sure all submodule commits used by the revisions to be
-       pushed are available on a remote tracking branch. If 'check' is
+       pushed are available on a remote-tracking branch. If 'check' is
        used git will verify that all submodule commits that changed in
        the revisions to be pushed are available on at least one remote
        of the submodule. If any commits are missing the push will be
index f5836e46d080d8f1f9931a8cd1aa22d0654341ba..5ce4cda8e72ff27a46cd79ae66e8587a9cc2b7d8 100644 (file)
@@ -98,6 +98,20 @@ advertised with this capability must cover all refs reported by
 the list command.  If no 'refspec' capability is advertised,
 there is an implied `refspec *:*`.
 
+'bidi-import'::
+       The fast-import commands 'cat-blob' and 'ls' can be used by remote-helpers
+       to retrieve information about blobs and trees that already exist in
+       fast-import's memory. This requires a channel from fast-import to the
+       remote-helper.
+       If it is advertised in addition to "import", git establishes a pipe from
+       fast-import to the remote-helper's stdin.
+       It follows that git and fast-import are both connected to the
+       remote-helper's stdin. Because git can send multiple commands to
+       the remote-helper it is required that helpers that use 'bidi-import'
+       buffer all 'import' commands of a batch before sending data to fast-import.
+       This is to prevent mixing commands and fast-import responses on the
+       helper's stdin.
+
 Capabilities for Pushing
 ~~~~~~~~~~~~~~~~~~~~~~~~
 'connect'::
@@ -286,7 +300,12 @@ terminated with a blank line. For each batch of 'import', the remote
 helper should produce a fast-import stream terminated by a 'done'
 command.
 +
-Supported if the helper has the "import" capability.
+Note that if the 'bidi-import' capability is used the complete batch
+sequence has to be buffered before starting to send data to fast-import
+to prevent mixing of commands and fast-import responses on the helper's
+stdin.
++
+Supported if the helper has the 'import' capability.
 
 'connect' <service>::
        Connects to given service. Standard input and standard output
index 5d31860eb1ddf57251d61d16b34693cebc4fd297..882cb1171bb361cd527cd71b8b06639237582dba 100644 (file)
@@ -107,6 +107,21 @@ as well as modifications of existing paths.
 Typically you would first remove all tracked files from the working
 tree using this command:
 
+Submodules
+~~~~~~~~~~
+Only submodules using a gitfile (which means they were cloned
+with a git version 1.7.8 or newer) will be removed from the work
+tree, as their repository lives inside the .git directory of the
+superproject. If a submodule (or one of those nested inside it)
+still uses a .git directory, `git rm` will fail - no matter if forced
+or not - to protect the submodule's history.
+
+A submodule is considered up-to-date when the HEAD is the same as
+recorded in the index, no tracked files are modified and no untracked
+files that aren't ignored are present in the submodules work tree.
+Ignored files are deemed expendable and won't stop a submodule's work
+tree from being removed.
+
 ----------------
 git ls-files -z | xargs -0 rm -f
 ----------------
index 67e5f53a9ef00991954b6bf95286a418c7e4af23..9f1ef9a463a8f884ba69474fdf5e1f338cc43bb9 100644 (file)
@@ -38,6 +38,9 @@ OPTIONS
        across git versions and regardless of user configuration. See
        below for details.
 
+--long::
+       Give the output in the long-format. This is the default.
+
 -u[<mode>]::
 --untracked-files[=<mode>]::
        Show untracked files.
index b4683bba1bc0ea6561a2378c663cf431776a3f7a..1d6527ab9f46fb0eb5d52ef335c1c4b4c377a72b 100644 (file)
@@ -9,7 +9,7 @@ git-submodule - Initialize, update or inspect submodules
 SYNOPSIS
 --------
 [verse]
-'git submodule' [--quiet] add [-b branch] [-f|--force]
+'git submodule' [--quiet] add [-b branch] [-f|--force] [--name <name>]
              [--reference <repository>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
@@ -265,6 +265,11 @@ OPTIONS
        Initialize all submodules for which "git submodule init" has not been
        called so far before updating.
 
+--name::
+       This option is only valid for the add command. It sets the submodule's
+       name to the given string instead of defaulting to its path. The name
+       must be valid as a directory name and may not end with a '/'.
+
 --reference <repository>::
        This option is only valid for add and update commands.  These
        commands sometimes need to clone a remote repository. In this case,
index 4effd789026e48085d6b0834cbef990968bedbb2..ab3e91c054ca7b4dcf7373b0aac839c18275a791 100644 (file)
@@ -18,7 +18,9 @@ working tree, is a text file with a syntax matching the requirements
 of linkgit:git-config[1].
 
 The file contains one subsection per submodule, and the subsection value
-is the name of the submodule. Each submodule section also contains the
+is the name of the submodule. The name is set to the path where the
+submodule has been added unless it was customized with the '--name'
+option of 'git submodule add'. Each submodule section also contains the
 following required keys:
 
 submodule.<name>.path::
index 861bd6f55352a12db6d1d680508e1d02a8e90d12..9bb4956ccdbef507ab115ac19dfe8ad326afb7c2 100644 (file)
@@ -9,11 +9,11 @@ merge.conflictstyle::
 merge.defaultToUpstream::
        If merge is called without any commit argument, merge the upstream
        branches configured for the current branch by using their last
-       observed values stored in their remote tracking branches.
+       observed values stored in their remote-tracking branches.
        The values of the `branch.<current branch>.merge` that name the
        branches at the remote named by `branch.<current branch>.remote`
        are consulted, and then they are mapped via `remote.<remote>.fetch`
-       to their corresponding remote tracking branches, and the tips of
+       to their corresponding remote-tracking branches, and the tips of
        these tracking branches are merged.
 
 merge.ff::
index ee497430cb0f8dc0859b90b8773e6b8e28d5e361..1ec14a068e750b114ac5f1ad2f468de8d0664a16 100644 (file)
@@ -79,6 +79,11 @@ if it is part of the log message.
 
        Match the regexp limiting patterns without regard to letters case.
 
+--basic-regexp::
+
+       Consider the limiting patterns to be basic regular expressions;
+       this is the default.
+
 -E::
 --extended-regexp::
 
@@ -91,6 +96,11 @@ if it is part of the log message.
        Consider the limiting patterns to be fixed strings (don't interpret
        pattern as a regular expression).
 
+--perl-regexp::
+
+       Consider the limiting patterns to be Perl-compatible regexp.
+       Requires libpcre to be compiled in.
+
 --remove-empty::
 
        Stop when a given path disappears from the tree.
index 1a797812fb426189b03a814498dd7019c96607b4..a959517b2339847e92464c86a73f9c472bfdb2d0 100644 (file)
@@ -53,3 +53,11 @@ Functions
 `argv_array_clear`::
        Free all memory associated with the array and return it to the
        initial, empty state.
+
+`argv_array_detach`::
+       Detach the argv array from the `struct argv_array`, transfering
+       ownership of the allocated array and strings.
+
+`argv_array_free_detached`::
+       Free the memory allocated by a `struct argv_array` that was later
+       detached and is now no longer needed.
index f69979e39e25bd0aae3dbdf21019d040aa1fd788..40829a9e994ef57e07bdcc16f0c6c8fd137f01fc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -495,6 +495,7 @@ PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
 PROGRAM_OBJS += show-index.o
 PROGRAM_OBJS += upload-pack.o
+PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -2449,6 +2450,10 @@ git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
+git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \
+       $(VCSSVN_LIB)
+
 $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
        $(QUIET_LNCP)$(RM) $@ && \
        ln $< $@ 2>/dev/null || \
index 256741d2262b237c56b6730bea9d52c9b39b7ee3..9e960d549c158a4c761942bb17824c1d5e075d49 100644 (file)
@@ -68,3 +68,23 @@ void argv_array_clear(struct argv_array *array)
        }
        argv_array_init(array);
 }
+
+const char **argv_array_detach(struct argv_array *array, int *argc)
+{
+       const char **argv =
+               array->argv == empty_argv || array->argc == 0 ? NULL : array->argv;
+       if (argc)
+               *argc = array->argc;
+       argv_array_init(array);
+       return argv;
+}
+
+void argv_array_free_detached(const char **argv)
+{
+       if (argv) {
+               int i;
+               for (i = 0; argv[i]; i++)
+                       free((char **)argv[i]);
+               free(argv);
+       }
+}
index f4b98660f8a98e2cecdf70285d1d76205b3f4be7..40248d424c31662a3a574cfea13635084feb14f5 100644 (file)
@@ -18,5 +18,7 @@ void argv_array_pushf(struct argv_array *, const char *fmt, ...);
 void argv_array_pushl(struct argv_array *, ...);
 void argv_array_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
+const char **argv_array_detach(struct argv_array *array, int *argc);
+void argv_array_free_detached(const char **argv);
 
 #endif /* ARGV_ARRAY_H */
diff --git a/attr.c b/attr.c
index 887a9ae46b7044489845abf2072339271caf2d4a..179886cc4b94a2c9260285b892eb200f6575453b 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -277,6 +277,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
 static struct attr_stack {
        struct attr_stack *prev;
        char *origin;
+       size_t originlen;
        unsigned num_matches;
        unsigned alloc;
        struct match_attr **attrs;
@@ -535,6 +536,7 @@ static void bootstrap_attr_stack(void)
        if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                elem = read_attr(GITATTRIBUTES_FILE, 1);
                elem->origin = xstrdup("");
+               elem->originlen = 0;
                elem->prev = attr_stack;
                attr_stack = elem;
                debug_push(elem);
@@ -628,7 +630,7 @@ static void prepare_attr_stack(const char *path)
                        strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
                        elem = read_attr(pathbuf.buf, 0);
                        strbuf_setlen(&pathbuf, cp - path);
-                       elem->origin = strbuf_detach(&pathbuf, NULL);
+                       elem->origin = strbuf_detach(&pathbuf, &elem->originlen);
                        elem->prev = attr_stack;
                        attr_stack = elem;
                        debug_push(elem);
@@ -645,13 +647,11 @@ static void prepare_attr_stack(const char *path)
 }
 
 static int path_matches(const char *pathname, int pathlen,
+                       const char *basename,
                        const char *pattern,
                        const char *base, int baselen)
 {
        if (!strchr(pattern, '/')) {
-               /* match basename */
-               const char *basename = strrchr(pathname, '/');
-               basename = basename ? basename + 1 : pathname;
                return (fnmatch_icase(pattern, basename, 0) == 0);
        }
        /*
@@ -693,7 +693,8 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
        return rem;
 }
 
-static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
+static int fill(const char *path, int pathlen, const char *basename,
+               struct attr_stack *stk, int rem)
 {
        int i;
        const char *base = stk->origin ? stk->origin : "";
@@ -702,8 +703,8 @@ static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
                struct match_attr *a = stk->attrs[i];
                if (a->is_macro)
                        continue;
-               if (path_matches(path, pathlen,
-                                a->u.pattern, base, strlen(base)))
+               if (path_matches(path, pathlen, basename,
+                                a->u.pattern, base, stk->originlen))
                        rem = fill_one("fill", a, rem);
        }
        return rem;
@@ -741,15 +742,19 @@ static void collect_all_attrs(const char *path)
 {
        struct attr_stack *stk;
        int i, pathlen, rem;
+       const char *basename;
 
        prepare_attr_stack(path);
        for (i = 0; i < attr_nr; i++)
                check_all_attr[i].value = ATTR__UNKNOWN;
 
+       basename = strrchr(path, '/');
+       basename = basename ? basename + 1 : path;
+
        pathlen = strlen(path);
        rem = attr_nr;
        for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
-               rem = fill(path, pathlen, stk, rem);
+               rem = fill(path, pathlen, basename, stk, rem);
 }
 
 int git_check_attr(const char *path, int num, struct git_attr_check *check)
index ffd26849c7ad20ff470323621486ae8a2aeb2501..1ec9c02612d0391984b484a5850f45c277a3e804 100644 (file)
@@ -154,10 +154,37 @@ static int branch_merged(int kind, const char *name,
        return merged;
 }
 
+static int check_branch_commit(const char *branchname, const char *refname,
+                              unsigned char *sha1, struct commit *head_rev,
+                              int kinds, int force)
+{
+       struct commit *rev = lookup_commit_reference(sha1);
+       if (!rev) {
+               error(_("Couldn't look up commit object for '%s'"), refname);
+               return -1;
+       }
+       if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
+               error(_("The branch '%s' is not fully merged.\n"
+                     "If you are sure you want to delete it, "
+                     "run 'git branch -D %s'."), branchname, branchname);
+               return -1;
+       }
+       return 0;
+}
+
+static void delete_branch_config(const char *branchname)
+{
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_addf(&buf, "branch.%s", branchname);
+       if (git_config_rename_section(buf.buf, NULL) < 0)
+               warning(_("Update of config-file failed"));
+       strbuf_release(&buf);
+}
+
 static int delete_branches(int argc, const char **argv, int force, int kinds,
                           int quiet)
 {
-       struct commit *rev, *head_rev = NULL;
+       struct commit *head_rev = NULL;
        unsigned char sha1[20];
        char *name = NULL;
        const char *fmt;
@@ -187,6 +214,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        die(_("Couldn't look up commit object for HEAD"));
        }
        for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+               const char *target;
+               int flags = 0;
+
                strbuf_branchname(&bname, argv[i]);
                if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
                        error(_("Cannot delete the branch '%s' "
@@ -198,7 +228,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                free(name);
 
                name = mkpathdup(fmt, bname.buf);
-               if (read_ref(name, sha1)) {
+               target = resolve_ref_unsafe(name, sha1, 0, &flags);
+               if (!target ||
+                   (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
                        error(remote_branch
                              ? _("remote branch '%s' not found.")
                              : _("branch '%s' not found."), bname.buf);
@@ -206,40 +238,31 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        continue;
                }
 
-               rev = lookup_commit_reference(sha1);
-               if (!rev) {
-                       error(_("Couldn't look up commit object for '%s'"), name);
-                       ret = 1;
-                       continue;
-               }
-
-               if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {
-                       error(_("The branch '%s' is not fully merged.\n"
-                             "If you are sure you want to delete it, "
-                             "run 'git branch -D %s'."), bname.buf, bname.buf);
+               if (!(flags & REF_ISSYMREF) &&
+                   check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
+                                       force)) {
                        ret = 1;
                        continue;
                }
 
-               if (delete_ref(name, sha1, 0)) {
+               if (delete_ref(name, sha1, REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote branch '%s'")
                              : _("Error deleting branch '%s'"),
                              bname.buf);
                        ret = 1;
-               } else {
-                       struct strbuf buf = STRBUF_INIT;
-                       if (!quiet)
-                               printf(remote_branch
-                                      ? _("Deleted remote branch %s (was %s).\n")
-                                      : _("Deleted branch %s (was %s).\n"),
-                                      bname.buf,
-                                      find_unique_abbrev(sha1, DEFAULT_ABBREV));
-                       strbuf_addf(&buf, "branch.%s", bname.buf);
-                       if (git_config_rename_section(buf.buf, NULL) < 0)
-                               warning(_("Update of config-file failed"));
-                       strbuf_release(&buf);
+                       continue;
+               }
+               if (!quiet) {
+                       printf(remote_branch
+                              ? _("Deleted remote branch %s (was %s).\n")
+                              : _("Deleted branch %s (was %s).\n"),
+                              bname.buf,
+                              (flags & REF_ISSYMREF)
+                              ? target
+                              : find_unique_abbrev(sha1, DEFAULT_ABBREV));
                }
+               delete_branch_config(bname.buf);
        }
 
        free(name);
index a17a5df4494e9414bd4b04fd008671d84d914ca0..1dd2ec5e6f878c02e2858d72f08d8b88d67bcd85 100644 (file)
@@ -112,10 +112,11 @@ static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
 static enum {
+       STATUS_FORMAT_NONE = 0,
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
        STATUS_FORMAT_PORCELAIN
-} status_format = STATUS_FORMAT_LONG;
+} status_format;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
@@ -454,6 +455,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s);
                break;
+       case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
                break;
@@ -1058,9 +1060,13 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
 
-       if (s->null_termination && status_format == STATUS_FORMAT_LONG)
-               status_format = STATUS_FORMAT_PORCELAIN;
-       if (status_format != STATUS_FORMAT_LONG)
+       if (s->null_termination) {
+               if (status_format == STATUS_FORMAT_NONE)
+                       status_format = STATUS_FORMAT_PORCELAIN;
+               else if (status_format == STATUS_FORMAT_LONG)
+                       die(_("--long and -z are incompatible"));
+       }
+       if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
 
        return argc;
@@ -1159,6 +1165,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"),
                            STATUS_FORMAT_PORCELAIN),
+               OPT_SET_INT(0, "long", &status_format,
+                           N_("show status in long format (default)"),
+                           STATUS_FORMAT_LONG),
                OPT_BOOLEAN('z', "null", &s.null_termination,
                            N_("terminate entries with NUL")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
@@ -1186,8 +1195,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                             builtin_status_usage, 0);
        finalize_colopts(&s.colopts, -1);
 
-       if (s.null_termination && status_format == STATUS_FORMAT_LONG)
-               status_format = STATUS_FORMAT_PORCELAIN;
+       if (s.null_termination) {
+               if (status_format == STATUS_FORMAT_NONE)
+                       status_format = STATUS_FORMAT_PORCELAIN;
+               else if (status_format == STATUS_FORMAT_LONG)
+                       die(_("--long and -z are incompatible"));
+       }
 
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
@@ -1216,6 +1229,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s);
                break;
+       case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
                s.ignore_submodule_arg = ignore_submodule_arg;
@@ -1386,6 +1400,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
+               OPT_SET_INT(0, "long", &status_format,
+                           N_("show status in long format (default)"),
+                           STATUS_FORMAT_LONG),
                OPT_BOOLEAN('z', "null", &s.null_termination,
                            N_("terminate entries with NUL")),
                OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
index 9fe11ed9de7b9e5c13187a630c45d8e44f50c851..04c185b1fbdc51ba9958fdb83d1c6616bcb0c18e 100644 (file)
@@ -144,7 +144,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
        if (!all && !might_be_tag)
                return 0;
 
-       if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
+       if (!peel_ref(path, peeled)) {
                is_tag = !!hashcmp(sha1, peeled);
        } else {
                hashcpy(peeled, sha1);
index 82530a61b483738382c810bcb141e33b06729d6f..0e1b6c860e18c6c47f343fad885cbd24f641862e 100644 (file)
@@ -86,7 +86,7 @@ static pthread_cond_t cond_result;
 static int skip_first_line;
 
 static void add_work(struct grep_opt *opt, enum grep_source_type type,
-                    const char *name, const void *id)
+                    const char *name, const char *path, const void *id)
 {
        grep_lock();
 
@@ -94,7 +94,7 @@ static void add_work(struct grep_opt *opt, enum grep_source_type type,
                pthread_cond_wait(&cond_write, &grep_mutex);
        }
 
-       grep_source_init(&todo[todo_end].source, type, name, id);
+       grep_source_init(&todo[todo_end].source, type, name, path, id);
        if (opt->binary != GREP_BINARY_TEXT)
                grep_source_load_driver(&todo[todo_end].source);
        todo[todo_end].done = 0;
@@ -261,103 +261,12 @@ static int wait_all(void)
 }
 #endif
 
-static int parse_pattern_type_arg(const char *opt, const char *arg)
+static int grep_cmd_config(const char *var, const char *value, void *cb)
 {
-       if (!strcmp(arg, "default"))
-               return GREP_PATTERN_TYPE_UNSPECIFIED;
-       else if (!strcmp(arg, "basic"))
-               return GREP_PATTERN_TYPE_BRE;
-       else if (!strcmp(arg, "extended"))
-               return GREP_PATTERN_TYPE_ERE;
-       else if (!strcmp(arg, "fixed"))
-               return GREP_PATTERN_TYPE_FIXED;
-       else if (!strcmp(arg, "perl"))
-               return GREP_PATTERN_TYPE_PCRE;
-       die("bad %s argument: %s", opt, arg);
-}
-
-static void grep_pattern_type_options(const int pattern_type, struct grep_opt *opt)
-{
-       switch (pattern_type) {
-       case GREP_PATTERN_TYPE_UNSPECIFIED:
-               /* fall through */
-
-       case GREP_PATTERN_TYPE_BRE:
-               opt->fixed = 0;
-               opt->pcre = 0;
-               opt->regflags &= ~REG_EXTENDED;
-               break;
-
-       case GREP_PATTERN_TYPE_ERE:
-               opt->fixed = 0;
-               opt->pcre = 0;
-               opt->regflags |= REG_EXTENDED;
-               break;
-
-       case GREP_PATTERN_TYPE_FIXED:
-               opt->fixed = 1;
-               opt->pcre = 0;
-               opt->regflags &= ~REG_EXTENDED;
-               break;
-
-       case GREP_PATTERN_TYPE_PCRE:
-               opt->fixed = 0;
-               opt->pcre = 1;
-               opt->regflags &= ~REG_EXTENDED;
-               break;
-       }
-}
-
-static int grep_config(const char *var, const char *value, void *cb)
-{
-       struct grep_opt *opt = cb;
-       char *color = NULL;
-
-       if (userdiff_config(var, value) < 0)
-               return -1;
-
-       if (!strcmp(var, "grep.extendedregexp")) {
-               if (git_config_bool(var, value))
-                       opt->extended_regexp_option = 1;
-               else
-                       opt->extended_regexp_option = 0;
-               return 0;
-       }
-
-       if (!strcmp(var, "grep.patterntype")) {
-               opt->pattern_type_option = parse_pattern_type_arg(var, value);
-               return 0;
-  }
-
-       if (!strcmp(var, "grep.linenumber")) {
-               opt->linenum = git_config_bool(var, value);
-               return 0;
-       }
-
-       if (!strcmp(var, "color.grep"))
-               opt->color = git_config_colorbool(var, value);
-       else if (!strcmp(var, "color.grep.context"))
-               color = opt->color_context;
-       else if (!strcmp(var, "color.grep.filename"))
-               color = opt->color_filename;
-       else if (!strcmp(var, "color.grep.function"))
-               color = opt->color_function;
-       else if (!strcmp(var, "color.grep.linenumber"))
-               color = opt->color_lineno;
-       else if (!strcmp(var, "color.grep.match"))
-               color = opt->color_match;
-       else if (!strcmp(var, "color.grep.selected"))
-               color = opt->color_selected;
-       else if (!strcmp(var, "color.grep.separator"))
-               color = opt->color_sep;
-       else
-               return git_color_default_config(var, value, cb);
-       if (color) {
-               if (!value)
-                       return config_error_nonbool(var);
-               color_parse(value, var, color);
-       }
-       return 0;
+       int st = grep_config(var, value, cb);
+       if (git_color_default_config(var, value, cb) < 0)
+               st = -1;
+       return st;
 }
 
 static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
@@ -371,7 +280,8 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type
 }
 
 static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
-                    const char *filename, int tree_name_len)
+                    const char *filename, int tree_name_len,
+                    const char *path)
 {
        struct strbuf pathbuf = STRBUF_INIT;
 
@@ -385,7 +295,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 
 #ifndef NO_PTHREADS
        if (use_threads) {
-               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
+               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
                strbuf_release(&pathbuf);
                return 0;
        } else
@@ -394,7 +304,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
                struct grep_source gs;
                int hit;
 
-               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
+               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
                strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
 
@@ -414,7 +324,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
 
 #ifndef NO_PTHREADS
        if (use_threads) {
-               add_work(opt, GREP_SOURCE_FILE, buf.buf, filename);
+               add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
                strbuf_release(&buf);
                return 0;
        } else
@@ -423,7 +333,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
                struct grep_source gs;
                int hit;
 
-               grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename);
+               grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
                strbuf_release(&buf);
                hit = grep_source(opt, &gs);
 
@@ -479,7 +389,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
                if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
                        if (ce_stage(ce))
                                continue;
-                       hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
+                       hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name);
                }
                else
                        hit |= grep_file(opt, ce->name);
@@ -497,7 +407,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
 }
 
 static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
-                    struct tree_desc *tree, struct strbuf *base, int tn_len)
+                    struct tree_desc *tree, struct strbuf *base, int tn_len,
+                    int check_attr)
 {
        int hit = 0;
        enum interesting match = entry_not_interesting;
@@ -518,7 +429,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                strbuf_add(base, entry.path, te_len);
 
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len);
+                       hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
+                                        check_attr ? base->buf + tn_len : NULL);
                }
                else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
@@ -533,7 +445,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
 
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
-                       hit |= grep_tree(opt, pathspec, &sub, base, tn_len);
+                       hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
+                                        check_attr);
                        free(data);
                }
                strbuf_setlen(base, old_baselen);
@@ -548,7 +461,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                       struct object *obj, const char *name)
 {
        if (obj->type == OBJ_BLOB)
-               return grep_sha1(opt, obj->sha1, name, 0);
+               return grep_sha1(opt, obj->sha1, name, 0, NULL);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@ -571,7 +484,8 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                        strbuf_addch(&base, ':');
                }
                init_tree_desc(&tree, data, size);
-               hit = grep_tree(opt, pathspec, &tree, &base, base.len);
+               hit = grep_tree(opt, pathspec, &tree, &base, base.len,
+                               obj->type == OBJ_COMMIT);
                strbuf_release(&base);
                free(data);
                return hit;
@@ -839,27 +753,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(grep_usage, options);
 
-       memset(&opt, 0, sizeof(opt));
-       opt.prefix = prefix;
-       opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
-       opt.relative = 1;
-       opt.pathname = 1;
-       opt.pattern_tail = &opt.pattern_list;
-       opt.header_tail = &opt.header_list;
-       opt.regflags = REG_NEWLINE;
-       opt.max_depth = -1;
-       opt.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
-       opt.extended_regexp_option = 0;
-
-       strcpy(opt.color_context, "");
-       strcpy(opt.color_filename, "");
-       strcpy(opt.color_function, "");
-       strcpy(opt.color_lineno, "");
-       strcpy(opt.color_match, GIT_COLOR_BOLD_RED);
-       strcpy(opt.color_selected, "");
-       strcpy(opt.color_sep, GIT_COLOR_CYAN);
-       opt.color = -1;
-       git_config(grep_config, &opt);
+       init_grep_defaults();
+       git_config(grep_cmd_config, NULL);
+       grep_init(&opt, prefix);
 
        /*
         * If there is no -- then the paths must exist in the working
@@ -875,13 +771,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
-
-       if (pattern_type_arg != GREP_PATTERN_TYPE_UNSPECIFIED)
-               grep_pattern_type_options(pattern_type_arg, &opt);
-       else if (opt.pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
-               grep_pattern_type_options(opt.pattern_type_option, &opt);
-       else if (opt.extended_regexp_option)
-               grep_pattern_type_options(GREP_PATTERN_TYPE_ERE, &opt);
+       grep_commit_pattern_type(pattern_type_arg, &opt);
 
        if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
index 09cf43e6d40efc13f6b7bda2094fca5bdc1bbc87..e7b7db1cacf1934a9bb0b96beb98ddda15a66074 100644 (file)
@@ -351,7 +351,8 @@ static int git_log_config(const char *var, const char *value, void *cb)
        }
        if (!prefixcmp(var, "color.decorate."))
                return parse_decorate_color_config(var, 15, value);
-
+       if (grep_config(var, value, cb) < 0)
+               return -1;
        return git_diff_ui_config(var, value, cb);
 }
 
@@ -360,6 +361,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        struct setup_revision_opt opt;
 
+       init_grep_defaults();
        git_config(git_log_config, NULL);
 
        init_revisions(&rev, prefix);
@@ -450,6 +452,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        struct pathspec match_all;
        int i, count, ret = 0;
 
+       init_grep_defaults();
        git_config(git_log_config, NULL);
 
        init_pathspec(&match_all, NULL);
@@ -530,6 +533,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        struct setup_revision_opt opt;
 
+       init_grep_defaults();
        git_config(git_log_config, NULL);
 
        init_revisions(&rev, prefix);
@@ -552,6 +556,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        struct setup_revision_opt opt;
 
+       init_grep_defaults();
        git_config(git_log_config, NULL);
 
        init_revisions(&rev, prefix);
@@ -1121,6 +1126,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        extra_hdr.strdup_strings = 1;
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
+       init_grep_defaults();
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
index 5e140640947cd9c38da00a01d8db9f036e8babd7..f069462cb03bbae46dae8d6b97420b3726690c47 100644 (file)
@@ -2033,7 +2033,6 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
 
        if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
            !peel_ref(path, peeled)        && /* peelable? */
-           !is_null_sha1(peeled)          && /* annotated tag? */
            locate_object_entry(peeled))      /* object packed? */
                add_object_entry(sha1, OBJ_TAG, NULL, 0);
        return 0;
index b384c4c3cfe973346b5b295416f3883ae2d73c94..2aea3b565318e11bdcc63f175631388f4f20ba44 100644 (file)
@@ -9,6 +9,7 @@
 #include "cache-tree.h"
 #include "tree-walk.h"
 #include "parse-options.h"
+#include "submodule.h"
 
 static const char * const builtin_rm_usage[] = {
        N_("git rm [options] [--] <file>..."),
@@ -17,9 +18,58 @@ static const char * const builtin_rm_usage[] = {
 
 static struct {
        int nr, alloc;
-       const char **name;
+       struct {
+               const char *name;
+               char is_submodule;
+       } *entry;
 } list;
 
+static int get_ours_cache_pos(const char *path, int pos)
+{
+       int i = -pos - 1;
+
+       while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
+               if (ce_stage(active_cache[i]) == 2)
+                       return i;
+               i++;
+       }
+       return -1;
+}
+
+static int check_submodules_use_gitfiles(void)
+{
+       int i;
+       int errs = 0;
+
+       for (i = 0; i < list.nr; i++) {
+               const char *name = list.entry[i].name;
+               int pos;
+               struct cache_entry *ce;
+               struct stat st;
+
+               pos = cache_name_pos(name, strlen(name));
+               if (pos < 0) {
+                       pos = get_ours_cache_pos(name, pos);
+                       if (pos < 0)
+                               continue;
+               }
+               ce = active_cache[pos];
+
+               if (!S_ISGITLINK(ce->ce_mode) ||
+                   (lstat(ce->name, &st) < 0) ||
+                   is_empty_dir(name))
+                       continue;
+
+               if (!submodule_uses_gitfile(name))
+                       errs = error(_("submodule '%s' (or one of its nested "
+                                    "submodules) uses a .git directory\n"
+                                    "(use 'rm -rf' if you really want to remove "
+                                    "it including all of its history)"), name);
+       }
+
+       return errs;
+}
+
 static int check_local_mod(unsigned char *head, int index_only)
 {
        /*
@@ -37,15 +87,26 @@ static int check_local_mod(unsigned char *head, int index_only)
                struct stat st;
                int pos;
                struct cache_entry *ce;
-               const char *name = list.name[i];
+               const char *name = list.entry[i].name;
                unsigned char sha1[20];
                unsigned mode;
                int local_changes = 0;
                int staged_changes = 0;
 
                pos = cache_name_pos(name, strlen(name));
-               if (pos < 0)
-                       continue; /* removing unmerged entry */
+               if (pos < 0) {
+                       /*
+                        * Skip unmerged entries except for populated submodules
+                        * that could lose history when removed.
+                        */
+                       pos = get_ours_cache_pos(name, pos);
+                       if (pos < 0)
+                               continue;
+
+                       if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
+                           is_empty_dir(name))
+                               continue;
+               }
                ce = active_cache[pos];
 
                if (lstat(ce->name, &st) < 0) {
@@ -58,9 +119,10 @@ static int check_local_mod(unsigned char *head, int index_only)
                        /* if a file was removed and it is now a
                         * directory, that is the same as ENOENT as
                         * far as git is concerned; we do not track
-                        * directories.
+                        * directories unless they are submodules.
                         */
-                       continue;
+                       if (!S_ISGITLINK(ce->ce_mode))
+                               continue;
                }
 
                /*
@@ -80,8 +142,11 @@ static int check_local_mod(unsigned char *head, int index_only)
 
                /*
                 * Is the index different from the file in the work tree?
+                * If it's a submodule, is its work tree modified?
                 */
-               if (ce_match_stat(ce, &st, 0))
+               if (ce_match_stat(ce, &st, 0) ||
+                   (S_ISGITLINK(ce->ce_mode) &&
+                    !ok_to_remove_submodule(ce->name)))
                        local_changes = 1;
 
                /*
@@ -115,10 +180,18 @@ static int check_local_mod(unsigned char *head, int index_only)
                                errs = error(_("'%s' has changes staged in the index\n"
                                             "(use --cached to keep the file, "
                                             "or -f to force removal)"), name);
-                       if (local_changes)
-                               errs = error(_("'%s' has local modifications\n"
-                                            "(use --cached to keep the file, "
-                                            "or -f to force removal)"), name);
+                       if (local_changes) {
+                               if (S_ISGITLINK(ce->ce_mode) &&
+                                   !submodule_uses_gitfile(name)) {
+                                       errs = error(_("submodule '%s' (or one of its nested "
+                                                    "submodules) uses a .git directory\n"
+                                                    "(use 'rm -rf' if you really want to remove "
+                                                    "it including all of its history)"), name);
+                               } else
+                                       errs = error(_("'%s' has local modifications\n"
+                                                    "(use --cached to keep the file, "
+                                                    "or -f to force removal)"), name);
+                       }
                }
        }
        return errs;
@@ -173,8 +246,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                struct cache_entry *ce = active_cache[i];
                if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        continue;
-               ALLOC_GROW(list.name, list.nr + 1, list.alloc);
-               list.name[list.nr++] = ce->name;
+               ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
+               list.entry[list.nr].name = ce->name;
+               list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);
        }
 
        if (pathspec) {
@@ -215,6 +289,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        hashclr(sha1);
                if (check_local_mod(sha1, index_only))
                        exit(1);
+       } else if (!index_only) {
+               if (check_submodules_use_gitfiles())
+                       exit(1);
        }
 
        /*
@@ -222,7 +299,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         * the index unless all of them succeed.
         */
        for (i = 0; i < list.nr; i++) {
-               const char *path = list.name[i];
+               const char *path = list.entry[i].name;
                if (!quiet)
                        printf("rm '%s'\n", path);
 
@@ -244,7 +321,25 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only) {
                int removed = 0;
                for (i = 0; i < list.nr; i++) {
-                       const char *path = list.name[i];
+                       const char *path = list.entry[i].name;
+                       if (list.entry[i].is_submodule) {
+                               if (is_empty_dir(path)) {
+                                       if (!rmdir(path)) {
+                                               removed = 1;
+                                               continue;
+                                       }
+                               } else {
+                                       struct strbuf buf = STRBUF_INIT;
+                                       strbuf_addstr(&buf, path);
+                                       if (!remove_dir_recursively(&buf, 0)) {
+                                               removed = 1;
+                                               strbuf_release(&buf);
+                                               continue;
+                                       }
+                                       strbuf_release(&buf);
+                                       /* Fallthrough and let remove_path() fail. */
+                               }
+                       }
                        if (!remove_path(path)) {
                                removed = 1;
                                continue;
index 4eb016d6e53628f19d51a50aeb074bf424154026..8d9b76a02fd115c0285216c27c5232f345d0582d 100644 (file)
@@ -28,7 +28,6 @@ static void show_one(const char *refname, const unsigned char *sha1)
 
 static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
 {
-       struct object *obj;
        const char *hex;
        unsigned char peeled[20];
 
@@ -79,25 +78,9 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
        if (!deref_tags)
                return 0;
 
-       if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
-               if (!is_null_sha1(peeled)) {
-                       hex = find_unique_abbrev(peeled, abbrev);
-                       printf("%s %s^{}\n", hex, refname);
-               }
-       }
-       else {
-               obj = parse_object(sha1);
-               if (!obj)
-                       die("git show-ref: bad ref %s (%s)", refname,
-                           sha1_to_hex(sha1));
-               if (obj->type == OBJ_TAG) {
-                       obj = deref_tag(obj, refname, 0);
-                       if (!obj)
-                               die("git show-ref: bad tag at ref %s (%s)", refname,
-                                   sha1_to_hex(sha1));
-                       hex = find_unique_abbrev(obj->sha1, abbrev);
-                       printf("%s %s^{}\n", hex, refname);
-               }
+       if (!peel_ref(refname, peeled)) {
+               hex = find_unique_abbrev(peeled, abbrev);
+               printf("%s %s^{}\n", hex, refname);
        }
        return 0;
 }
index afc892d6b1837d807490f42790bf29686d074a10..4e6383898c59f03127ed698abcb86857d63a8eac 100644 (file)
@@ -335,6 +335,28 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
        return freopen(filename, otype, stream);
 }
 
+#undef fflush
+int mingw_fflush(FILE *stream)
+{
+       int ret = fflush(stream);
+
+       /*
+        * write() is used behind the scenes of stdio output functions.
+        * Since git code does not check for errors after each stdio write
+        * operation, it can happen that write() is called by a later
+        * stdio function even if an earlier write() call failed. In the
+        * case of a pipe whose readable end was closed, only the first
+        * call to write() reports EPIPE on Windows. Subsequent write()
+        * calls report EINVAL. It is impossible to notice whether this
+        * fflush invocation triggered such a case, therefore, we have to
+        * catch all EINVAL errors whole-sale.
+        */
+       if (ret && errno == EINVAL)
+               errno = EPIPE;
+
+       return ret;
+}
+
 /*
  * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
  * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
index 61a652138ac7b7ef412bbc15e6ce479db158b181..eeb08d120bab73154afe282873514c920628eab2 100644 (file)
@@ -185,6 +185,9 @@ FILE *mingw_fopen (const char *filename, const char *otype);
 FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
 #define freopen mingw_freopen
 
+int mingw_fflush(FILE *stream);
+#define fflush mingw_fflush
+
 char *mingw_getcwd(char *pointer, int len);
 #define getcwd mingw_getcwd
 
index 08e47e2e48a5bd310025752ee5ecdce81721b34d..fff8a43bc0378d7d7ee6d7ab3deee228c87b1d5c 100644 (file)
--- a/config.c
+++ b/config.c
@@ -10,8 +10,6 @@
 #include "strbuf.h"
 #include "quote.h"
 
-#define MAXNAME (256)
-
 typedef struct config_file {
        struct config_file *prev;
        FILE *f;
@@ -19,7 +17,7 @@ typedef struct config_file {
        int linenr;
        int eof;
        struct strbuf value;
-       char var[MAXNAME];
+       struct strbuf var;
 } config_file;
 
 static config_file *cf;
@@ -260,7 +258,7 @@ static inline int iskeychar(int c)
        return isalnum(c) || c == '-';
 }
 
-static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
+static int get_value(config_fn_t fn, void *data, struct strbuf *name)
 {
        int c;
        char *value;
@@ -272,11 +270,9 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
                        break;
                if (!iskeychar(c))
                        break;
-               name[len++] = tolower(c);
-               if (len >= MAXNAME)
-                       return -1;
+               strbuf_addch(name, tolower(c));
        }
-       name[len] = 0;
+
        while (c == ' ' || c == '\t')
                c = get_next_char();
 
@@ -288,10 +284,10 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
                if (!value)
                        return -1;
        }
-       return fn(name, value, data);
+       return fn(name->buf, value, data);
 }
 
-static int get_extended_base_var(char *name, int baselen, int c)
+static int get_extended_base_var(struct strbuf *name, int c)
 {
        do {
                if (c == '\n')
@@ -302,7 +298,7 @@ static int get_extended_base_var(char *name, int baselen, int c)
        /* We require the format to be '[base "extension"]' */
        if (c != '"')
                return -1;
-       name[baselen++] = '.';
+       strbuf_addch(name, '.');
 
        for (;;) {
                int c = get_next_char();
@@ -315,37 +311,31 @@ static int get_extended_base_var(char *name, int baselen, int c)
                        if (c == '\n')
                                goto error_incomplete_line;
                }
-               name[baselen++] = c;
-               if (baselen > MAXNAME / 2)
-                       return -1;
+               strbuf_addch(name, c);
        }
 
        /* Final ']' */
        if (get_next_char() != ']')
                return -1;
-       return baselen;
+       return 0;
 error_incomplete_line:
        cf->linenr--;
        return -1;
 }
 
-static int get_base_var(char *name)
+static int get_base_var(struct strbuf *name)
 {
-       int baselen = 0;
-
        for (;;) {
                int c = get_next_char();
                if (cf->eof)
                        return -1;
                if (c == ']')
-                       return baselen;
+                       return 0;
                if (isspace(c))
-                       return get_extended_base_var(name, baselen, c);
+                       return get_extended_base_var(name, c);
                if (!iskeychar(c) && c != '.')
                        return -1;
-               if (baselen > MAXNAME / 2)
-                       return -1;
-               name[baselen++] = tolower(c);
+               strbuf_addch(name, tolower(c));
        }
 }
 
@@ -353,7 +343,7 @@ static int git_parse_file(config_fn_t fn, void *data)
 {
        int comment = 0;
        int baselen = 0;
-       char *var = cf->var;
+       struct strbuf *var = &cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -389,17 +379,24 @@ static int git_parse_file(config_fn_t fn, void *data)
                        continue;
                }
                if (c == '[') {
-                       baselen = get_base_var(var);
-                       if (baselen <= 0)
+                       /* Reset prior to determining a new stem */
+                       strbuf_reset(var);
+                       if (get_base_var(var) < 0 || var->len < 1)
                                break;
-                       var[baselen++] = '.';
-                       var[baselen] = 0;
+                       strbuf_addch(var, '.');
+                       baselen = var->len;
                        continue;
                }
                if (!isalpha(c))
                        break;
-               var[baselen] = tolower(c);
-               if (get_value(fn, data, var, baselen+1) < 0)
+               /*
+                * Truncate the var name back to the section header
+                * stem prior to grabbing the suffix part of the name
+                * and the value.
+                */
+               strbuf_setlen(var, baselen);
+               strbuf_addch(var, tolower(c));
+               if (get_value(fn, data, var) < 0)
                        break;
        }
        die("bad config file line %d in %s", cf->linenr, cf->name);
@@ -899,12 +896,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
                top.linenr = 1;
                top.eof = 0;
                strbuf_init(&top.value, 1024);
+               strbuf_init(&top.var, 1024);
                cf = &top;
 
                ret = git_parse_file(fn, data);
 
                /* pop config-file parsing state stack */
                strbuf_release(&top.value);
+               strbuf_release(&top.var);
                cf = top.prev;
 
                fclose(f);
index 35db24f5eae600a973391915ceb61c4635941aec..f363505abb278f89a94413874099e0e80ee8192c 100644 (file)
@@ -10,7 +10,8 @@ int main(int argc, char **argv)
 {
        if (svndump_init(NULL))
                return 1;
-       svndump_read((argc > 1) ? argv[1] : NULL);
+       svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master",
+                       "refs/notes/svn/revs");
        svndump_deinit();
        svndump_reset();
        return 0;
diff --git a/contrib/svn-fe/svnrdump_sim.py b/contrib/svn-fe/svnrdump_sim.py
new file mode 100755 (executable)
index 0000000..1cfac4a
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+"""
+Simulates svnrdump by replaying an existing dump from a file, taking care
+of the specified revision range.
+To simulate incremental imports the environment variable SVNRMAX can be set
+to the highest revision that should be available.
+"""
+import sys, os
+
+
+def getrevlimit():
+        var = 'SVNRMAX'
+        if os.environ.has_key(var):
+                return os.environ[var]
+        return None
+
+def writedump(url, lower, upper):
+        if url.startswith('sim://'):
+                filename = url[6:]
+                if filename[-1] == '/': filename = filename[:-1] #remove terminating slash
+        else:
+                raise ValueError('sim:// url required')
+        f = open(filename, 'r');
+        state = 'header'
+        wroterev = False
+        while(True):
+                l = f.readline()
+                if l == '': break
+                if state == 'header' and l.startswith('Revision-number: '):
+                        state = 'prefix'
+                if state == 'prefix' and l == 'Revision-number: %s\n' % lower:
+                        state = 'selection'
+                if not upper == 'HEAD' and state == 'selection' and l == 'Revision-number: %s\n' % upper:
+                        break;
+
+                if state == 'header' or state == 'selection':
+                        if state == 'selection': wroterev = True
+                        sys.stdout.write(l)
+        return wroterev
+
+if __name__ == "__main__":
+        if not (len(sys.argv) in (3, 4, 5)):
+                print "usage: %s dump URL -rLOWER:UPPER"
+                sys.exit(1)
+        if not sys.argv[1] == 'dump': raise NotImplementedError('only "dump" is suppported.')
+        url = sys.argv[2]
+        r = ('0', 'HEAD')
+        if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
+                r = sys.argv[3][2:].lstrip().split(':')
+        if not getrevlimit() is None: r[1] = getrevlimit()
+        if writedump(url, r[0], r[1]): ret = 0
+        else: ret = 1
+        sys.exit(ret)
diff --git a/diff.c b/diff.c
index 35d3f073859acf66dbd38c6096c0cc47bd26b6e2..86e5f2a4a881afd48563e63b0891495be12b82f0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -26,6 +26,7 @@ static int diff_detect_rename_default;
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
+static int diff_context_default = 3;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
@@ -141,6 +142,12 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_use_color_default = git_config_colorbool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.context")) {
+               diff_context_default = git_config_int(var, value);
+               if (diff_context_default < 0)
+                       return -1;
+               return 0;
+       }
        if (!strcmp(var, "diff.renames")) {
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
@@ -3170,7 +3177,7 @@ void diff_setup(struct diff_options *options)
        options->break_opt = -1;
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
-       options->context = 3;
+       options->context = diff_context_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
 
        options->change = diff_change;
index 178e45305d2b66bbe5a0c0902d989ea66888869b..53142492afcfb92f0453359af9a00a045c51e7e9 100755 (executable)
@@ -64,37 +64,19 @@ EOF
 
 eval "$functions"
 
-# When piped a commit, output a script to set the ident of either
-# "author" or "committer
+finish_ident() {
+       # Ensure non-empty id name.
+       echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
+       # And make sure everything is exported.
+       echo "export GIT_$1_NAME"
+       echo "export GIT_$1_EMAIL"
+       echo "export GIT_$1_DATE"
+}
 
 set_ident () {
-       lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
-       uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
-       pick_id_script='
-               /^'$lid' /{
-                       s/'\''/'\''\\'\'\''/g
-                       h
-                       s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
-                       s/'\''/'\''\'\'\''/g
-                       s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
-
-                       g
-                       s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
-                       s/'\''/'\''\'\'\''/g
-                       s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
-
-                       g
-                       s/^'$lid' [^<]* <[^>]*> \(.*\)$/@\1/
-                       s/'\''/'\''\'\'\''/g
-                       s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
-
-                       q
-               }
-       '
-
-       LANG=C LC_ALL=C sed -ne "$pick_id_script"
-       # Ensure non-empty id name.
-       echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
+       parse_ident_from_commit author AUTHOR committer COMMITTER
+       finish_ident AUTHOR
+       finish_ident COMMITTER
 }
 
 USAGE="[--env-filter <command>] [--tree-filter <command>]
@@ -320,10 +302,8 @@ while read commit parents; do
        git cat-file commit "$commit" >../commit ||
                die "Cannot read commit $commit"
 
-       eval "$(set_ident AUTHOR <../commit)" ||
-               die "setting author failed for commit $commit"
-       eval "$(set_ident COMMITTER <../commit)" ||
-               die "setting committer failed for commit $commit"
+       eval "$(set_ident <../commit)" ||
+               die "setting author/committer failed for commit $commit"
        eval "$filter_env" < /dev/null ||
                die "env filter failed: $filter_env"
 
index ee0e0bc045bb7d92045a3afb0a042748834e6163..22f0aed6db366c3d4a291b3ae646c45002ff86c1 100644 (file)
@@ -191,28 +191,52 @@ require_clean_work_tree () {
        fi
 }
 
+# Generate a sed script to parse identities from a commit.
+#
+# Reads the commit from stdin, which should be in raw format (e.g., from
+# cat-file or "--pretty=raw").
+#
+# The first argument specifies the ident line to parse (e.g., "author"), and
+# the second specifies the environment variable to put it in (e.g., "AUTHOR"
+# for "GIT_AUTHOR_*"). Multiple pairs can be given to parse author and
+# committer.
+pick_ident_script () {
+       while test $# -gt 0
+       do
+               lid=$1; shift
+               uid=$1; shift
+               printf '%s' "
+               /^$lid /{
+                       s/'/'\\\\''/g
+                       h
+                       s/^$lid "'\([^<]*\) <[^>]*> .*$/\1/'"
+                       s/.*/GIT_${uid}_NAME='&'/p
+
+                       g
+                       s/^$lid "'[^<]* <\([^>]*\)> .*$/\1/'"
+                       s/.*/GIT_${uid}_EMAIL='&'/p
+
+                       g
+                       s/^$lid "'[^<]* <[^>]*> \(.*\)$/@\1/'"
+                       s/.*/GIT_${uid}_DATE='&'/p
+               }
+               "
+       done
+       echo '/^$/q'
+}
+
+# Create a pick-script as above and feed it to sed. Stdout is suitable for
+# feeding to eval.
+parse_ident_from_commit () {
+       LANG=C LC_ALL=C sed -ne "$(pick_ident_script "$@")"
+}
+
+# Parse the author from a commit given as an argument. Stdout is suitable for
+# feeding to eval to set the usual GIT_* ident variables.
 get_author_ident_from_commit () {
-       pick_author_script='
-       /^author /{
-               s/'\''/'\''\\'\'\''/g
-               h
-               s/^author \([^<]*\) <[^>]*> .*$/\1/
-               s/.*/GIT_AUTHOR_NAME='\''&'\''/p
-
-               g
-               s/^author [^<]* <\([^>]*\)> .*$/\1/
-               s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
-
-               g
-               s/^author [^<]* <[^>]*> \(.*\)$/@\1/
-               s/.*/GIT_AUTHOR_DATE='\''&'\''/p
-
-               q
-       }
-       '
        encoding=$(git config i18n.commitencoding || echo UTF-8)
        git show -s --pretty=raw --encoding="$encoding" "$1" -- |
-       LANG=C LC_ALL=C sed -ne "$pick_author_script"
+       parse_ident_from_commit author AUTHOR
 }
 
 # Clear repo-local GIT_* environment variables. Useful when switching to
index ab6b1107b6090494f192f361471ed5748ffa7dc1..9e1e1f44106ca659ee705b4f66dc5cd4997ec907 100755 (executable)
@@ -5,7 +5,7 @@
 # Copyright (c) 2007 Lars Hjemli
 
 dashless=$(basename "$0" | sed -e 's/-/ /')
-USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
+USAGE="[--quiet] add [-b branch] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] init [--] [<path>...]
    or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
@@ -29,6 +29,7 @@ files=
 nofetch=
 update=
 prefix=
+custom_name=
 
 # The function takes at most 2 arguments. The first argument is the
 # URL that navigates to the submodule origin repo. When relative, this URL
@@ -179,8 +180,9 @@ module_name()
 module_clone()
 {
        sm_path=$1
-       url=$2
-       reference="$3"
+       name=$2
+       url=$3
+       reference="$4"
        quiet=
        if test -n "$GIT_QUIET"
        then
@@ -189,8 +191,6 @@ module_clone()
 
        gitdir=
        gitdir_base=
-       name=$(module_name "$sm_path" 2>/dev/null)
-       test -n "$name" || name="$sm_path"
        base_name=$(dirname "$name")
 
        gitdir=$(git rev-parse --git-dir)
@@ -272,6 +272,11 @@ cmd_add()
                        reference="$1"
                        shift
                        ;;
+               --name)
+                       case "$2" in '') usage ;; esac
+                       custom_name=$2
+                       shift
+                       ;;
                --)
                        shift
                        break
@@ -336,6 +341,13 @@ Use -f if you really want to add it." >&2
                exit 1
        fi
 
+       if test -n "$custom_name"
+       then
+               sm_name="$custom_name"
+       else
+               sm_name="$sm_path"
+       fi
+
        # perhaps the path exists and is already a git repo, else clone it
        if test -e "$sm_path"
        then
@@ -347,8 +359,21 @@ Use -f if you really want to add it." >&2
                fi
 
        else
-
-               module_clone "$sm_path" "$realrepo" "$reference" || exit
+               if test -d ".git/modules/$sm_name"
+               then
+                       if test -z "$force"
+                       then
+                               echo >&2 "$(eval_gettext "A git directory for '\$sm_name' is found locally with remote(s):")"
+                               GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,"  ", -e s,' (fetch)',, >&2
+                               echo >&2 "$(eval_gettext "If you want to reuse this local git directory instead of cloning again from")"
+                               echo >&2 "  $realrepo"
+                               echo >&2 "$(eval_gettext "use the '--force' option. If the local git directory is not the correct repo")"
+                               die "$(eval_gettext "or you are unsure what this means choose another name with the '--name' option.")"
+                       else
+                               echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
+                       fi
+               fi
+               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
                (
                        clear_local_git_env
                        cd "$sm_path" &&
@@ -359,13 +384,13 @@ Use -f if you really want to add it." >&2
                        esac
                ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
        fi
-       git config submodule."$sm_path".url "$realrepo"
+       git config submodule."$sm_name".url "$realrepo"
 
        git add $force "$sm_path" ||
        die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
 
-       git config -f .gitmodules submodule."$sm_path".path "$sm_path" &&
-       git config -f .gitmodules submodule."$sm_path".url "$repo" &&
+       git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
+       git config -f .gitmodules submodule."$sm_name".url "$repo" &&
        git add --force .gitmodules ||
        die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
 }
@@ -594,7 +619,7 @@ Maybe you want to use 'update --init'?")"
 
                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$url" "$reference"|| exit
+                       module_clone "$sm_path" "$name" "$url" "$reference" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
diff --git a/grep.c b/grep.c
index edc7776677ed201dbe117c207a680e1f832737e3..4bd1b8b1dd67e4f5c2223536b435198ce67463ba 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -6,6 +6,183 @@
 static int grep_source_load(struct grep_source *gs);
 static int grep_source_is_binary(struct grep_source *gs);
 
+static struct grep_opt grep_defaults;
+
+/*
+ * Initialize the grep_defaults template with hardcoded defaults.
+ * We could let the compiler do this, but without C99 initializers
+ * the code gets unwieldy and unreadable, so...
+ */
+void init_grep_defaults(void)
+{
+       struct grep_opt *opt = &grep_defaults;
+       static int run_once;
+
+       if (run_once)
+               return;
+       run_once++;
+
+       memset(opt, 0, sizeof(*opt));
+       opt->relative = 1;
+       opt->pathname = 1;
+       opt->regflags = REG_NEWLINE;
+       opt->max_depth = -1;
+       opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
+       opt->extended_regexp_option = 0;
+       strcpy(opt->color_context, "");
+       strcpy(opt->color_filename, "");
+       strcpy(opt->color_function, "");
+       strcpy(opt->color_lineno, "");
+       strcpy(opt->color_match, GIT_COLOR_BOLD_RED);
+       strcpy(opt->color_selected, "");
+       strcpy(opt->color_sep, GIT_COLOR_CYAN);
+       opt->color = -1;
+}
+
+static int parse_pattern_type_arg(const char *opt, const char *arg)
+{
+       if (!strcmp(arg, "default"))
+               return GREP_PATTERN_TYPE_UNSPECIFIED;
+       else if (!strcmp(arg, "basic"))
+               return GREP_PATTERN_TYPE_BRE;
+       else if (!strcmp(arg, "extended"))
+               return GREP_PATTERN_TYPE_ERE;
+       else if (!strcmp(arg, "fixed"))
+               return GREP_PATTERN_TYPE_FIXED;
+       else if (!strcmp(arg, "perl"))
+               return GREP_PATTERN_TYPE_PCRE;
+       die("bad %s argument: %s", opt, arg);
+}
+
+/*
+ * Read the configuration file once and store it in
+ * the grep_defaults template.
+ */
+int grep_config(const char *var, const char *value, void *cb)
+{
+       struct grep_opt *opt = &grep_defaults;
+       char *color = NULL;
+
+       if (userdiff_config(var, value) < 0)
+               return -1;
+
+       if (!strcmp(var, "grep.extendedregexp")) {
+               if (git_config_bool(var, value))
+                       opt->extended_regexp_option = 1;
+               else
+                       opt->extended_regexp_option = 0;
+               return 0;
+       }
+
+       if (!strcmp(var, "grep.patterntype")) {
+               opt->pattern_type_option = parse_pattern_type_arg(var, value);
+               return 0;
+       }
+
+       if (!strcmp(var, "grep.linenumber")) {
+               opt->linenum = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!strcmp(var, "color.grep"))
+               opt->color = git_config_colorbool(var, value);
+       else if (!strcmp(var, "color.grep.context"))
+               color = opt->color_context;
+       else if (!strcmp(var, "color.grep.filename"))
+               color = opt->color_filename;
+       else if (!strcmp(var, "color.grep.function"))
+               color = opt->color_function;
+       else if (!strcmp(var, "color.grep.linenumber"))
+               color = opt->color_lineno;
+       else if (!strcmp(var, "color.grep.match"))
+               color = opt->color_match;
+       else if (!strcmp(var, "color.grep.selected"))
+               color = opt->color_selected;
+       else if (!strcmp(var, "color.grep.separator"))
+               color = opt->color_sep;
+
+       if (color) {
+               if (!value)
+                       return config_error_nonbool(var);
+               color_parse(value, var, color);
+       }
+       return 0;
+}
+
+/*
+ * Initialize one instance of grep_opt and copy the
+ * default values from the template we read the configuration
+ * information in an earlier call to git_config(grep_config).
+ */
+void grep_init(struct grep_opt *opt, const char *prefix)
+{
+       struct grep_opt *def = &grep_defaults;
+
+       memset(opt, 0, sizeof(*opt));
+       opt->prefix = prefix;
+       opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
+       opt->pattern_tail = &opt->pattern_list;
+       opt->header_tail = &opt->header_list;
+
+       opt->color = def->color;
+       opt->extended_regexp_option = def->extended_regexp_option;
+       opt->pattern_type_option = def->pattern_type_option;
+       opt->linenum = def->linenum;
+       opt->max_depth = def->max_depth;
+       opt->pathname = def->pathname;
+       opt->regflags = def->regflags;
+       opt->relative = def->relative;
+
+       strcpy(opt->color_context, def->color_context);
+       strcpy(opt->color_filename, def->color_filename);
+       strcpy(opt->color_function, def->color_function);
+       strcpy(opt->color_lineno, def->color_lineno);
+       strcpy(opt->color_match, def->color_match);
+       strcpy(opt->color_selected, def->color_selected);
+       strcpy(opt->color_sep, def->color_sep);
+}
+
+void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+{
+       if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_set_pattern_type_option(pattern_type, opt);
+       else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_set_pattern_type_option(opt->pattern_type_option, opt);
+       else if (opt->extended_regexp_option)
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
+}
+
+void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+{
+       switch (pattern_type) {
+       case GREP_PATTERN_TYPE_UNSPECIFIED:
+               /* fall through */
+
+       case GREP_PATTERN_TYPE_BRE:
+               opt->fixed = 0;
+               opt->pcre = 0;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_ERE:
+               opt->fixed = 0;
+               opt->pcre = 0;
+               opt->regflags |= REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_FIXED:
+               opt->fixed = 1;
+               opt->pcre = 0;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_PCRE:
+               opt->fixed = 0;
+               opt->pcre = 1;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+       }
+}
 
 static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
                                        const char *origin, int no,
@@ -1373,7 +1550,7 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
        struct grep_source gs;
        int r;
 
-       grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL);
+       grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL, NULL);
        gs.buf = buf;
        gs.size = size;
 
@@ -1384,10 +1561,12 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
 }
 
 void grep_source_init(struct grep_source *gs, enum grep_source_type type,
-                     const char *name, const void *identifier)
+                     const char *name, const char *path,
+                     const void *identifier)
 {
        gs->type = type;
        gs->name = name ? xstrdup(name) : NULL;
+       gs->path = path ? xstrdup(path) : NULL;
        gs->buf = NULL;
        gs->size = 0;
        gs->driver = NULL;
@@ -1409,6 +1588,8 @@ void grep_source_clear(struct grep_source *gs)
 {
        free(gs->name);
        gs->name = NULL;
+       free(gs->path);
+       gs->path = NULL;
        free(gs->identifier);
        gs->identifier = NULL;
        grep_source_clear_data(gs);
@@ -1501,7 +1682,8 @@ void grep_source_load_driver(struct grep_source *gs)
                return;
 
        grep_attr_lock();
-       gs->driver = userdiff_find_by_path(gs->name);
+       if (gs->path)
+               gs->driver = userdiff_find_by_path(gs->path);
        if (!gs->driver)
                gs->driver = userdiff_find_by_name("default");
        grep_attr_unlock();
diff --git a/grep.h b/grep.h
index c256ac6e15b22e153623ae3395fb6ffc732b20b9..8fc854f400802c3329fc3c028942de55b820ac90 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -138,6 +138,12 @@ struct grep_opt {
        void *output_priv;
 };
 
+extern void init_grep_defaults(void);
+extern int grep_config(const char *var, const char *value, void *);
+extern void grep_init(struct grep_opt *, const char *prefix);
+void grep_set_pattern_type_option(enum grep_pattern_type, struct grep_opt *opt);
+void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
+
 extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
 extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
@@ -158,11 +164,13 @@ struct grep_source {
        char *buf;
        unsigned long size;
 
+       char *path; /* for attribute lookups */
        struct userdiff_driver *driver;
 };
 
 void grep_source_init(struct grep_source *gs, enum grep_source_type type,
-                     const char *name, const void *identifier);
+                     const char *name, const char *path,
+                     const void *identifier);
 void grep_source_clear_data(struct grep_source *gs);
 void grep_source_clear(struct grep_source *gs);
 void grep_source_load_driver(struct grep_source *gs);
diff --git a/http.c b/http.c
index df9bb710863e30ba98d7661f84e872b30ed4449e..98c0b2b362c531234ac9d02d922d23958f6d90cf 100644 (file)
--- a/http.c
+++ b/http.c
@@ -745,8 +745,7 @@ char *get_remote_object_url(const char *url, const char *hex,
        return strbuf_detach(&buf, NULL);
 }
 
-int handle_curl_result(struct active_request_slot *slot,
-                      struct slot_results *results)
+int handle_curl_result(struct slot_results *results)
 {
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
@@ -759,7 +758,6 @@ int handle_curl_result(struct active_request_slot *slot,
                        return HTTP_NOAUTH;
                } else {
                        credential_fill(&http_auth);
-                       init_curl_http_auth(slot->curl);
                        return HTTP_REAUTH;
                }
        } else {
@@ -821,7 +819,7 @@ static int http_request(const char *url, void *result, int target, int options)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               ret = handle_curl_result(slot, &results);
+               ret = handle_curl_result(&results);
        } else {
                error("Unable to start HTTP request for %s", url);
                ret = HTTP_START_FAILED;
diff --git a/http.h b/http.h
index 0bd1e849e1406c8c8a822c6ecc6de84d4b5e1798..0a80d303e369b4adf77ca131c732ba39a990610d 100644 (file)
--- a/http.h
+++ b/http.h
@@ -78,8 +78,7 @@ extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
-extern int handle_curl_result(struct active_request_slot *slot,
-                             struct slot_results *results);
+extern int handle_curl_result(struct slot_results *results);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
index 1a45c1b0c5128335fc340e7d6409b4e772419a07..295361a8aa7d075bbb9fdb8173858e37f9a43211 100644 (file)
@@ -1,5 +1,30 @@
 diff_cmd () {
+       # p4merge does not like /dev/null
+       rm_local=
+       rm_remote=
+       if test "/dev/null" = "$LOCAL"
+       then
+               LOCAL="./p4merge-dev-null.LOCAL.$$"
+               >"$LOCAL"
+               rm_local=true
+       fi
+       if test "/dev/null" = "$REMOTE"
+       then
+               REMOTE="./p4merge-dev-null.REMOTE.$$"
+               >"$REMOTE"
+               rm_remote=true
+       fi
+
        "$merge_tool_path" "$LOCAL" "$REMOTE"
+
+       if test -n "$rm_local"
+       then
+               rm -f "$LOCAL"
+       fi
+       if test -n "$rm_remote"
+       then
+               rm -f "$REMOTE"
+       fi
 }
 
 merge_cmd () {
diff --git a/pager.c b/pager.c
index 4dcb08d9674c252cbe2c2725f92eca2a0350dc93..c0b4387d969476232a4e00a7acf9b06dc4ef6edc 100644 (file)
--- a/pager.c
+++ b/pager.c
  * something different on Windows.
  */
 
-#ifndef WIN32
-static void pager_preexec(void)
-{
-       /*
-        * Work around bug in "less" by not starting it until we
-        * have real input
-        */
-       fd_set in;
-
-       FD_ZERO(&in);
-       FD_SET(0, &in);
-       select(1, &in, NULL, &in, NULL);
-}
-#endif
-
 static const char *pager_argv[] = { NULL, NULL };
 static struct child_process pager_process;
 
@@ -93,9 +78,6 @@ void setup_pager(void)
                static const char *env[] = { "LESS=FRSX", NULL };
                pager_process.env = env;
        }
-#ifndef WIN32
-       pager_process.preexec_cb = pager_preexec;
-#endif
        if (start_command(&pager_process))
                return;
 
diff --git a/refs.c b/refs.c
index da74a2b29a20a2c94fef61641e35980007c1ab95..02e47b1157c367f9233ae88870b9a43cbe1a7d52 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1202,6 +1202,8 @@ int peel_ref(const char *refname, unsigned char *sha1)
        if (current_ref && (current_ref->name == refname
                || !strcmp(current_ref->name, refname))) {
                if (current_ref->flag & REF_KNOWS_PEELED) {
+                       if (is_null_sha1(current_ref->u.value.peeled))
+                           return -1;
                        hashcpy(sha1, current_ref->u.value.peeled);
                        return 0;
                }
@@ -1223,9 +1225,16 @@ int peel_ref(const char *refname, unsigned char *sha1)
        }
 
 fallback:
-       o = parse_object(base);
-       if (o && o->type == OBJ_TAG) {
-               o = deref_tag(o, refname, 0);
+       o = lookup_unknown_object(base);
+       if (o->type == OBJ_NONE) {
+               int type = sha1_object_info(base, NULL);
+               if (type < 0)
+                       return -1;
+               o->type = type;
+       }
+
+       if (o->type == OBJ_TAG) {
+               o = deref_tag_noverify(o);
                if (o) {
                        hashcpy(sha1, o->sha1);
                        return 0;
index 42716c59cfe419571d98a7f3b3674e94e2fe3618..278fecdd9bd00bf0df0a3d1f35777546794bc92f 100644 (file)
@@ -356,7 +356,7 @@ static int run_slot(struct active_request_slot *slot)
        slot->curl_result = curl_easy_perform(slot->curl);
        finish_active_slot(slot);
 
-       err = handle_curl_result(slot, &results);
+       err = handle_curl_result(&results);
        if (err != HTTP_OK && err != HTTP_REAUTH) {
                error("RPC failed; result=%d, HTTP code = %ld",
                      results.curl_result, results.http_code);
@@ -431,6 +431,11 @@ static int post_rpc(struct rpc_state *rpc)
                        return -1;
        }
 
+       headers = curl_slist_append(headers, rpc->hdr_content_type);
+       headers = curl_slist_append(headers, rpc->hdr_accept);
+       headers = curl_slist_append(headers, "Expect:");
+
+retry:
        slot = get_active_slot();
 
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
@@ -438,10 +443,6 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
 
-       headers = curl_slist_append(headers, rpc->hdr_content_type);
-       headers = curl_slist_append(headers, rpc->hdr_accept);
-       headers = curl_slist_append(headers, "Expect:");
-
        if (large_request) {
                /* The request body is large and the size cannot be predicted.
                 * We must use chunked encoding to send it.
@@ -515,9 +516,9 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
 
-       do {
-               err = run_slot(slot);
-       } while (err == HTTP_REAUTH && !large_request && !use_gzip);
+       err = run_slot(slot);
+       if (err == HTTP_REAUTH && !large_request && !use_gzip)
+               goto retry;
        if (err != HTTP_OK)
                err = -1;
 
diff --git a/remote-testsvn.c b/remote-testsvn.c
new file mode 100644 (file)
index 0000000..51fba05
--- /dev/null
@@ -0,0 +1,342 @@
+#include "cache.h"
+#include "remote.h"
+#include "strbuf.h"
+#include "url.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "vcs-svn/svndump.h"
+#include "notes.h"
+#include "argv-array.h"
+
+static const char *url;
+static int dump_from_file;
+static const char *private_ref;
+static const char *remote_ref = "refs/heads/master";
+static const char *marksfilename, *notes_ref;
+struct rev_note { unsigned int rev_nr; };
+
+static int cmd_capabilities(const char *line);
+static int cmd_import(const char *line);
+static int cmd_list(const char *line);
+
+typedef int (*input_command_handler)(const char *);
+struct input_command_entry {
+       const char *name;
+       input_command_handler fn;
+       unsigned char batchable;        /* whether the command starts or is part of a batch */
+};
+
+static const struct input_command_entry input_command_list[] = {
+       { "capabilities", cmd_capabilities, 0 },
+       { "import", cmd_import, 1 },
+       { "list", cmd_list, 0 },
+       { NULL, NULL }
+};
+
+static int cmd_capabilities(const char *line)
+{
+       printf("import\n");
+       printf("bidi-import\n");
+       printf("refspec %s:%s\n\n", remote_ref, private_ref);
+       fflush(stdout);
+       return 0;
+}
+
+static void terminate_batch(void)
+{
+       /* terminate a current batch's fast-import stream */
+       printf("done\n");
+       fflush(stdout);
+}
+
+/* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
+static char *read_ref_note(const unsigned char sha1[20])
+{
+       const unsigned char *note_sha1;
+       char *msg = NULL;
+       unsigned long msglen;
+       enum object_type type;
+
+       init_notes(NULL, notes_ref, NULL, 0);
+       if (!(note_sha1 = get_note(NULL, sha1)))
+               return NULL;    /* note tree not found */
+       if (!(msg = read_sha1_file(note_sha1, &type, &msglen)))
+               error("Empty notes tree. %s", notes_ref);
+       else if (!msglen || type != OBJ_BLOB) {
+               error("Note contains unusable content. "
+                       "Is something else using this notes tree? %s", notes_ref);
+               free(msg);
+               msg = NULL;
+       }
+       free_notes(NULL);
+       return msg;
+}
+
+static int parse_rev_note(const char *msg, struct rev_note *res)
+{
+       const char *key, *value, *end;
+       size_t len;
+
+       while (*msg) {
+               end = strchr(msg, '\n');
+               len = end ? end - msg : strlen(msg);
+
+               key = "Revision-number: ";
+               if (!prefixcmp(msg, key)) {
+                       long i;
+                       char *end;
+                       value = msg + strlen(key);
+                       i = strtol(value, &end, 0);
+                       if (end == value || i < 0 || i > UINT32_MAX)
+                               return -1;
+                       res->rev_nr = i;
+               }
+               msg += len + 1;
+       }
+       return 0;
+}
+
+static int note2mark_cb(const unsigned char *object_sha1,
+               const unsigned char *note_sha1, char *note_path,
+               void *cb_data)
+{
+       FILE *file = (FILE *)cb_data;
+       char *msg;
+       unsigned long msglen;
+       enum object_type type;
+       struct rev_note note;
+
+       if (!(msg = read_sha1_file(note_sha1, &type, &msglen)) ||
+                       !msglen || type != OBJ_BLOB) {
+               free(msg);
+               return 1;
+       }
+       if (parse_rev_note(msg, &note))
+               return 2;
+       if (fprintf(file, ":%d %s\n", note.rev_nr, sha1_to_hex(object_sha1)) < 1)
+               return 3;
+       return 0;
+}
+
+static void regenerate_marks(void)
+{
+       int ret;
+       FILE *marksfile = fopen(marksfilename, "w+");
+
+       if (!marksfile)
+               die_errno("Couldn't create mark file %s.", marksfilename);
+       ret = for_each_note(NULL, 0, note2mark_cb, marksfile);
+       if (ret)
+               die("Regeneration of marks failed, returned %d.", ret);
+       fclose(marksfile);
+}
+
+static void check_or_regenerate_marks(int latestrev)
+{
+       FILE *marksfile;
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf line = STRBUF_INIT;
+       int found = 0;
+
+       if (latestrev < 1)
+               return;
+
+       init_notes(NULL, notes_ref, NULL, 0);
+       marksfile = fopen(marksfilename, "r");
+       if (!marksfile) {
+               regenerate_marks();
+               marksfile = fopen(marksfilename, "r");
+               if (!marksfile)
+                       die_errno("cannot read marks file %s!", marksfilename);
+               fclose(marksfile);
+       } else {
+               strbuf_addf(&sb, ":%d ", latestrev);
+               while (strbuf_getline(&line, marksfile, '\n') != EOF) {
+                       if (!prefixcmp(line.buf, sb.buf)) {
+                               found++;
+                               break;
+                       }
+               }
+               fclose(marksfile);
+               if (!found)
+                       regenerate_marks();
+       }
+       free_notes(NULL);
+       strbuf_release(&sb);
+       strbuf_release(&line);
+}
+
+static int cmd_import(const char *line)
+{
+       int code;
+       int dumpin_fd;
+       char *note_msg;
+       unsigned char head_sha1[20];
+       unsigned int startrev;
+       struct argv_array svndump_argv = ARGV_ARRAY_INIT;
+       struct child_process svndump_proc;
+
+       if (read_ref(private_ref, head_sha1))
+               startrev = 0;
+       else {
+               note_msg = read_ref_note(head_sha1);
+               if(note_msg == NULL) {
+                       warning("No note found for %s.", private_ref);
+                       startrev = 0;
+               } else {
+                       struct rev_note note = { 0 };
+                       if (parse_rev_note(note_msg, &note))
+                               die("Revision number couldn't be parsed from note.");
+                       startrev = note.rev_nr + 1;
+                       free(note_msg);
+               }
+       }
+       check_or_regenerate_marks(startrev - 1);
+
+       if (dump_from_file) {
+               dumpin_fd = open(url, O_RDONLY);
+               if(dumpin_fd < 0)
+                       die_errno("Couldn't open svn dump file %s.", url);
+       } else {
+               memset(&svndump_proc, 0, sizeof(struct child_process));
+               svndump_proc.out = -1;
+               argv_array_push(&svndump_argv, "svnrdump");
+               argv_array_push(&svndump_argv, "dump");
+               argv_array_push(&svndump_argv, url);
+               argv_array_pushf(&svndump_argv, "-r%u:HEAD", startrev);
+               svndump_proc.argv = svndump_argv.argv;
+
+               code = start_command(&svndump_proc);
+               if (code)
+                       die("Unable to start %s, code %d", svndump_proc.argv[0], code);
+               dumpin_fd = svndump_proc.out;
+       }
+       /* setup marks file import/export */
+       printf("feature import-marks-if-exists=%s\n"
+                       "feature export-marks=%s\n", marksfilename, marksfilename);
+
+       svndump_init_fd(dumpin_fd, STDIN_FILENO);
+       svndump_read(url, private_ref, notes_ref);
+       svndump_deinit();
+       svndump_reset();
+
+       close(dumpin_fd);
+       if (!dump_from_file) {
+               code = finish_command(&svndump_proc);
+               if (code)
+                       warning("%s, returned %d", svndump_proc.argv[0], code);
+               argv_array_clear(&svndump_argv);
+       }
+
+       return 0;
+}
+
+static int cmd_list(const char *line)
+{
+       printf("? %s\n\n", remote_ref);
+       fflush(stdout);
+       return 0;
+}
+
+static int do_command(struct strbuf *line)
+{
+       const struct input_command_entry *p = input_command_list;
+       static struct string_list batchlines = STRING_LIST_INIT_DUP;
+       static const struct input_command_entry *batch_cmd;
+       /*
+        * commands can be grouped together in a batch.
+        * Batches are ended by \n. If no batch is active the program ends.
+        * During a batch all lines are buffered and passed to the handler function
+        * when the batch is terminated.
+        */
+       if (line->len == 0) {
+               if (batch_cmd) {
+                       struct string_list_item *item;
+                       for_each_string_list_item(item, &batchlines)
+                               batch_cmd->fn(item->string);
+                       terminate_batch();
+                       batch_cmd = NULL;
+                       string_list_clear(&batchlines, 0);
+                       return 0;       /* end of the batch, continue reading other commands. */
+               }
+               return 1;       /* end of command stream, quit */
+       }
+       if (batch_cmd) {
+               if (prefixcmp(batch_cmd->name, line->buf))
+                       die("Active %s batch interrupted by %s", batch_cmd->name, line->buf);
+               /* buffer batch lines */
+               string_list_append(&batchlines, line->buf);
+               return 0;
+       }
+
+       for (p = input_command_list; p->name; p++) {
+               if (!prefixcmp(line->buf, p->name) && (strlen(p->name) == line->len ||
+                               line->buf[strlen(p->name)] == ' ')) {
+                       if (p->batchable) {
+                               batch_cmd = p;
+                               string_list_append(&batchlines, line->buf);
+                               return 0;
+                       }
+                       return p->fn(line->buf);
+               }
+       }
+       die("Unknown command '%s'\n", line->buf);
+       return 0;
+}
+
+int main(int argc, const char **argv)
+{
+       struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
+                       private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
+                       notes_ref_sb = STRBUF_INIT;
+       static struct remote *remote;
+       const char *url_in;
+
+       git_extract_argv0_path(argv[0]);
+       setup_git_directory();
+       if (argc < 2 || argc > 3) {
+               usage("git-remote-svn <remote-name> [<url>]");
+               return 1;
+       }
+
+       remote = remote_get(argv[1]);
+       url_in = (argc == 3) ? argv[2] : remote->url[0];
+
+       if (!prefixcmp(url_in, "file://")) {
+               dump_from_file = 1;
+               url = url_decode(url_in + sizeof("file://")-1);
+       } else {
+               dump_from_file = 0;
+               end_url_with_slash(&url_sb, url_in);
+               url = url_sb.buf;
+       }
+
+       strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);
+       private_ref = private_ref_sb.buf;
+
+       strbuf_addf(&notes_ref_sb, "refs/notes/%s/revs", remote->name);
+       notes_ref = notes_ref_sb.buf;
+
+       strbuf_addf(&marksfilename_sb, "%s/info/fast-import/remote-svn/%s.marks",
+               get_git_dir(), remote->name);
+       marksfilename = marksfilename_sb.buf;
+
+       while (1) {
+               if (strbuf_getline(&buf, stdin, '\n') == EOF) {
+                       if (ferror(stdin))
+                               die("Error reading command stream");
+                       else
+                               die("Unexpected end of command stream");
+               }
+               if (do_command(&buf))
+                       break;
+               strbuf_reset(&buf);
+       }
+
+       strbuf_release(&buf);
+       strbuf_release(&url_sb);
+       strbuf_release(&private_ref_sb);
+       strbuf_release(&notes_ref_sb);
+       strbuf_release(&marksfilename_sb);
+       return 0;
+}
index a09e60bedbbbf2c6bdeeacc3d1032bcc1be9cb55..d7d621cdbf224f5a2bc1780e9602dcd3d08c1d57 100644 (file)
@@ -1048,9 +1048,9 @@ void init_revisions(struct rev_info *revs, const char *prefix)
 
        revs->commit_format = CMIT_FMT_DEFAULT;
 
+       init_grep_defaults();
+       grep_init(&revs->grep_filter, prefix);
        revs->grep_filter.status_only = 1;
-       revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
-       revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
 
        diff_setup(&revs->diffopt);
@@ -1603,13 +1603,17 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return argcount;
        } else if (!strcmp(arg, "--grep-debug")) {
                revs->grep_filter.debug = 1;
+       } else if (!strcmp(arg, "--basic-regexp")) {
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
-               revs->grep_filter.regflags |= REG_EXTENDED;
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter);
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
                DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
-               revs->grep_filter.fixed = 1;
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter);
+       } else if (!strcmp(arg, "--perl-regexp")) {
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
        } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
@@ -1893,6 +1897,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        revs->diffopt.abbrev = revs->abbrev;
        diff_setup_done(&revs->diffopt);
 
+       grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
+                                &revs->grep_filter);
        compile_grep_patterns(&revs->grep_filter);
 
        if (revs->reverse && revs->reflog_info)
index 1101ef72378a502b1681a8e1bc306f38027974ae..3b982e4d55274ebe87c5751fa198bee53e371d9b 100644 (file)
@@ -397,16 +397,6 @@ int start_command(struct child_process *cmd)
                                        unsetenv(*cmd->env);
                        }
                }
-               if (cmd->preexec_cb) {
-                       /*
-                        * We cannot predict what the pre-exec callback does.
-                        * Forgo parent notification.
-                        */
-                       close(child_notifier);
-                       child_notifier = -1;
-
-                       cmd->preexec_cb();
-               }
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
                } else if (cmd->use_shell) {
index 44f7d2bd42ddcf2d65722b7e54bea69645d167a2..850c638f19a2b6776c0311db28371b18438f207a 100644 (file)
@@ -39,7 +39,6 @@ struct child_process {
        unsigned stdout_to_stderr:1;
        unsigned use_shell:1;
        unsigned clean_on_exit:1;
-       void (*preexec_cb)(void);
 };
 
 int start_command(struct child_process *);
index 0510f76c24b3b3ce66baa0f05054d3f01b960b6e..4b9e30cadcd1ff45e7d4f23b210b22006445da91 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -44,7 +44,9 @@ void strbuf_release(struct strbuf *sb)
 
 char *strbuf_detach(struct strbuf *sb, size_t *sz)
 {
-       char *res = sb->alloc ? sb->buf : NULL;
+       char *res;
+       strbuf_grow(sb, 0);
+       res = sb->buf;
        if (sz)
                *sz = sb->len;
        strbuf_init(sb, 0);
index 50f213e926374e86c23cdbfc730f6d81af4816b8..e3e0b455eae925996aa8ecf8bf931edd0638d1cc 100644 (file)
@@ -759,6 +759,86 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        return dirty_submodule;
 }
 
+int submodule_uses_gitfile(const char *path)
+{
+       struct child_process cp;
+       const char *argv[] = {
+               "submodule",
+               "foreach",
+               "--quiet",
+               "--recursive",
+               "test -f .git",
+               NULL,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir;
+
+       strbuf_addf(&buf, "%s/.git", path);
+       git_dir = read_gitfile(buf.buf);
+       if (!git_dir) {
+               strbuf_release(&buf);
+               return 0;
+       }
+       strbuf_release(&buf);
+
+       /* Now test that all nested submodules use a gitfile too */
+       memset(&cp, 0, sizeof(cp));
+       cp.argv = argv;
+       cp.env = local_repo_env;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.no_stderr = 1;
+       cp.no_stdout = 1;
+       cp.dir = path;
+       if (run_command(&cp))
+               return 0;
+
+       return 1;
+}
+
+int ok_to_remove_submodule(const char *path)
+{
+       struct stat st;
+       ssize_t len;
+       struct child_process cp;
+       const char *argv[] = {
+               "status",
+               "--porcelain",
+               "-u",
+               "--ignore-submodules=none",
+               NULL,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       int ok_to_remove = 1;
+
+       if ((lstat(path, &st) < 0) || is_empty_dir(path))
+               return 1;
+
+       if (!submodule_uses_gitfile(path))
+               return 0;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.argv = argv;
+       cp.env = local_repo_env;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.out = -1;
+       cp.dir = path;
+       if (start_command(&cp))
+               die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path);
+
+       len = strbuf_read(&buf, cp.out, 1024);
+       if (len > 2)
+               ok_to_remove = 0;
+       close(cp.out);
+
+       if (finish_command(&cp))
+               die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path);
+
+       strbuf_release(&buf);
+       return ok_to_remove;
+}
+
 static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
 {
index 594b50d51066be35d2f9dc9aa795f3ecdf131573..f2e8271fc68c3b1e78d200e2af374a77f253f1ee 100644 (file)
@@ -28,6 +28,8 @@ int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet);
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
+int submodule_uses_gitfile(const char *path);
+int ok_to_remove_submodule(const char *path);
 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], int search);
 int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
index 79c8d0142ea72d25a2c403738312a01d0ace50cb..80e6be39d7b76d8e5daa6cad24a22a80a42e8eff 100755 (executable)
@@ -262,6 +262,25 @@ test_expect_success 'config information was renamed, too' \
        "test $(git config branch.s.dummy) = Hello &&
         test_must_fail git config branch.s/s/dummy"
 
+test_expect_success 'deleting a symref' '
+       git branch target &&
+       git symbolic-ref refs/heads/symref refs/heads/target &&
+       echo "Deleted branch symref (was refs/heads/target)." >expect &&
+       git branch -d symref >actual &&
+       test_path_is_file .git/refs/heads/target &&
+       test_path_is_missing .git/refs/heads/symref &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'deleting a dangling symref' '
+       git symbolic-ref refs/heads/dangling-symref nowhere &&
+       test_path_is_file .git/refs/heads/dangling-symref &&
+       echo "Deleted branch dangling-symref (was nowhere)." >expect &&
+       git branch -d dangling-symref >actual &&
+       test_path_is_missing .git/refs/heads/dangling-symref &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'renaming a symref is not allowed' \
 '
        git symbolic-ref refs/heads/master2 refs/heads/master &&
index 9fd28bcf3435f831fa24285dd703c82b8396d635..97254e8d337ff066f633ece5d1a7c8a59108e703 100755 (executable)
@@ -262,4 +262,347 @@ test_expect_success 'rm removes subdirectories recursively' '
        ! test -d dir
 '
 
+cat >expect <<EOF
+D  submod
+EOF
+
+cat >expect.modified <<EOF
+ M submod
+EOF
+
+test_expect_success 'rm removes empty submodules from work tree' '
+       mkdir submod &&
+       git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&
+       git config -f .gitmodules submodule.sub.url ./. &&
+       git config -f .gitmodules submodule.sub.path submod &&
+       git submodule init &&
+       git add .gitmodules &&
+       git commit -m "add submodule" &&
+       git rm submod &&
+       test ! -e submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm removes removed submodule from index' '
+       git reset --hard &&
+       git submodule update &&
+       rm -rf submod &&
+       git rm submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm removes work tree of unmodified submodules' '
+       git reset --hard &&
+       git submodule update &&
+       git rm submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with different HEAD fails unless forced' '
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               git checkout HEAD^
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with modifications fails unless forced' '
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               echo X >empty
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with untracked files fails unless forced' '
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               echo X >untracked
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup submodule conflict' '
+       git reset --hard &&
+       git submodule update &&
+       git checkout -b branch1 &&
+       echo 1 >nitfol &&
+       git add nitfol &&
+       git commit -m "added nitfol 1" &&
+       git checkout -b branch2 master &&
+       echo 2 >nitfol &&
+       git add nitfol &&
+       git commit -m "added nitfol 2" &&
+       git checkout -b conflict1 master &&
+       (cd submod &&
+               git fetch &&
+               git checkout branch1
+       ) &&
+       git add submod &&
+       git commit -m "submod 1" &&
+       git checkout -b conflict2 master &&
+       (cd submod &&
+               git checkout branch2
+       ) &&
+       git add submod &&
+       git commit -m "submod 2"
+'
+
+cat >expect.conflict <<EOF
+UU submod
+EOF
+
+test_expect_success 'rm removes work tree of unmodified conflicted submodule' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       git submodule update &&
+       test_must_fail git merge conflict2 &&
+       git rm submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with different HEAD fails unless forced' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               git checkout HEAD^
+       ) &&
+       test_must_fail git merge conflict2 &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.conflict actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               echo X >empty
+       ) &&
+       test_must_fail git merge conflict2 &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.conflict actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               echo X >untracked
+       ) &&
+       test_must_fail git merge conflict2 &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.conflict actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with a .git directory fails even when forced' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               rm .git &&
+               cp -a ../.git/modules/sub .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       ) &&
+       test_must_fail git merge conflict2 &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -d submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.conflict actual &&
+       test_must_fail git rm -f submod &&
+       test -d submod &&
+       test -d submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.conflict actual &&
+       git merge --abort &&
+       rm -rf submod
+'
+
+test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
+       git checkout conflict1 &&
+       git reset --hard &&
+       test_must_fail git merge conflict2 &&
+       git rm submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' '
+       git checkout -f master &&
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               rm .git &&
+               cp -a ../.git/modules/sub .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -d submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       ! test -s actual &&
+       test_must_fail git rm -f submod &&
+       test -d submod &&
+       test -d submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       ! test -s actual &&
+       rm -rf submod
+'
+
+cat >expect.deepmodified <<EOF
+ M submod/subsubmod
+EOF
+
+test_expect_success 'setup subsubmodule' '
+       git reset --hard &&
+       git submodule update &&
+       (cd submod &&
+               git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod &&
+               git config -f .gitmodules submodule.sub.url ../. &&
+               git config -f .gitmodules submodule.sub.path subsubmod &&
+               git submodule init &&
+               git add .gitmodules &&
+               git commit -m "add subsubmodule" &&
+               git submodule update subsubmod
+       ) &&
+       git commit -a -m "added deep submodule"
+'
+
+test_expect_success 'rm recursively removes work tree of unmodified submodules' '
+       git rm submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with different nested HEAD fails unless forced' '
+       git reset --hard &&
+       git submodule update --recursive &&
+       (cd submod/subsubmod &&
+               git checkout HEAD^
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with nested modifications fails unless forced' '
+       git reset --hard &&
+       git submodule update --recursive &&
+       (cd submod/subsubmod &&
+               echo X >empty
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with nested untracked files fails unless forced' '
+       git reset --hard &&
+       git submodule update --recursive &&
+       (cd submod/subsubmod &&
+               echo X >untracked
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect.modified actual &&
+       git rm -f submod &&
+       test ! -d submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with a nested .git directory fails even when forced' '
+       git reset --hard &&
+       git submodule update --recursive &&
+       (cd submod/subsubmod &&
+               rm .git &&
+               cp -a ../../.git/modules/sub/modules/sub .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       ) &&
+       test_must_fail git rm submod &&
+       test -d submod &&
+       test -d submod/subsubmod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       ! test -s actual &&
+       test_must_fail git rm -f submod &&
+       test -d submod &&
+       test -d submod/subsubmod/.git &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       ! test -s actual &&
+       rm -rf submod
+'
+
 test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
new file mode 100755 (executable)
index 0000000..97172b4
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Mozilla Foundation
+#
+
+test_description='diff.context configuration'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       cat >template <<-\EOF &&
+       firstline
+       b
+       c
+       d
+       e
+       f
+       preline
+       TARGET
+       postline
+       i
+       j
+       k
+       l
+       m
+       n
+       EOF
+       sed "/TARGET/d" >x <template &&
+       git update-index --add x &&
+       git commit -m initial &&
+
+       sed "s/TARGET/ADDED/" >x <template &&
+       git update-index --add x &&
+       git commit -m next &&
+
+       sed "s/TARGET/MODIFIED/" >x <template
+'
+
+test_expect_success 'the default number of context lines is 3' '
+       git diff >output &&
+       ! grep "^ d" output &&
+       grep "^ e" output &&
+       grep "^ j" output &&
+       ! grep "^ k" output
+'
+
+test_expect_success 'diff.context honored by "log"' '
+       git log -1 -p >output &&
+       ! grep firstline output &&
+       git config diff.context 8 &&
+       git log -1 -p >output &&
+       grep "^ firstline" output
+'
+
+test_expect_success 'The -U option overrides diff.context' '
+       git config diff.context 8 &&
+       git log -U4 -1 >output &&
+       ! grep "^ firstline" output
+'
+
+test_expect_success 'diff.context honored by "diff"' '
+       git config diff.context 8 &&
+       git diff >output &&
+       grep "^ firstline" output
+'
+
+test_expect_success 'plumbing not affected' '
+       git config diff.context 8 &&
+       git diff-files -p >output &&
+       ! grep "^ firstline" output
+'
+
+test_expect_success 'non-integer config parsing' '
+       git config diff.context no &&
+       test_must_fail git diff 2>output &&
+       test_i18ngrep "bad config value" output
+'
+
+test_expect_success 'negative integer config parsing' '
+       git config diff.context -1 &&
+       test_must_fail git diff 2>output &&
+       test_i18ngrep "bad config file" output
+'
+
+test_expect_success '-U0 is valid, so is diff.context=0' '
+       git config diff.context 0 &&
+       git diff >output &&
+       grep "^-ADDED" output &&
+       grep "^+MODIFIED" output
+'
+
+test_done
index 924ba536ca9718da0f4126d53c316a5f7a1926a5..e6537abe1d611aa2d96215fb263f76c5c0cf3b1a 100755 (executable)
@@ -230,6 +230,12 @@ test_expect_success 'log --grep -i' '
        test_cmp expect actual
 '
 
+test_expect_success 'log -F -E --grep=<ere> uses ere' '
+       echo second >expect &&
+       git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual &&
+       test_cmp expect actual
+'
+
 cat > expect <<EOF
 * Second
 * sixth
index 4d13e10de1f2594ab304b3a2b7b5905d75d6ff84..1e7a209efa715bc52d14d7f653ecfc13ffb5301f 100755 (executable)
@@ -167,10 +167,11 @@ test_expect_success 'author information is preserved' '
        test_tick &&
        GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
        git branch preserved-author &&
-       git filter-branch -f --msg-filter "cat; \
+       (sane_unset GIT_AUTHOR_NAME &&
+        git filter-branch -f --msg-filter "cat; \
                        test \$GIT_COMMIT != $(git rev-parse master) || \
                        echo Hallo" \
-               preserved-author &&
+               preserved-author) &&
        test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
 '
 
index fd6410fc7149ed381d4d09120089859281e40696..26f831984d603a959e2141641269268a2f0d78c8 100755 (executable)
@@ -111,6 +111,28 @@ test_expect_success 'grep respects binary diff attribute' '
        test_cmp expect actual
 '
 
+test_expect_success 'grep --cached respects binary diff attribute' '
+       git grep --cached text t >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep --cached respects binary diff attribute (2)' '
+       git add .gitattributes &&
+       rm .gitattributes &&
+       git grep --cached text t >actual &&
+       test_when_finished "git rm --cached .gitattributes" &&
+       test_when_finished "git checkout .gitattributes" &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep revision respects binary diff attribute' '
+       git commit -m new &&
+       echo "Binary file HEAD:t matches" >expect &&
+       git grep text HEAD -- t >actual &&
+       test_when_finished "git reset HEAD^" &&
+       test_cmp expect actual
+'
+
 test_expect_success 'grep respects not-binary diff attribute' '
        echo binQary | q_to_nul >b &&
        git add b &&
index 53970374913eaa6f1b5921b14b5f25043974e3a4..de7d45352e001d3c1b2c7f24361dc38dd9e0ea0a 100755 (executable)
@@ -681,4 +681,79 @@ test_expect_success 'moving the superproject does not break submodules' '
        )
 '
 
+test_expect_success 'submodule add --name allows to replace a submodule with another at the same path' '
+       (
+               cd addtest2 &&
+               (
+                       cd repo &&
+                       echo "$submodurl/repo" >expect &&
+                       git config remote.origin.url >actual &&
+                       test_cmp expect actual &&
+                       echo "gitdir: ../.git/modules/repo" >expect &&
+                       test_cmp expect .git
+               ) &&
+               rm -rf repo &&
+               git rm repo &&
+               git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
+               test ! -s actual &&
+               echo "gitdir: ../.git/modules/submod" >expect &&
+               test_cmp expect submod/.git &&
+               (
+                       cd repo &&
+                       echo "$submodurl/bare.git" >expect &&
+                       git config remote.origin.url >actual &&
+                       test_cmp expect actual &&
+                       echo "gitdir: ../.git/modules/repo_new" >expect &&
+                       test_cmp expect .git
+               ) &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo.path >actual &&
+               test_cmp expect actual &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/repo" >expect &&
+               git config -f .gitmodules submodule.repo.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/repo" >expect &&
+               git config submodule.repo.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'submodule add with an existing name fails unless forced' '
+       (
+               cd addtest2 &&
+               rm -rf repo &&
+               git rm repo &&
+               test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo &&
+               test ! -d repo &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/bare.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               git submodule add -f -q --name repo_new "$submodurl/repo.git" repo &&
+               test -d repo &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/repo.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/repo.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 15426530e49ef2e42cda2528f4a72a784eb40cd9..feaec6cdf4b8c7eac348ea1de61887c93c69244d 100755 (executable)
@@ -627,7 +627,7 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'
        (cd super &&
         git reset --hard master &&
         rm -rf deeper/ &&
-        git submodule add ../submodule deeper/submodule
+        git submodule add --force ../submodule deeper/submodule
        )
 '
 
diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh
new file mode 100755 (executable)
index 0000000..4f2dfe0
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='tests remote-svn'
+
+. ./test-lib.sh
+
+MARKSPATH=.git/info/fast-import/remote-svn
+
+if ! test_have_prereq PYTHON
+then
+       skip_all='skipping remote-svn tests, python not available'
+       test_done
+fi
+
+# We override svnrdump by placing a symlink to the svnrdump-emulator in .
+export PATH="$HOME:$PATH"
+ln -sf $GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py "$HOME/svnrdump"
+
+init_git () {
+       rm -fr .git &&
+       git init &&
+       #git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump
+       # let's reuse an exisiting dump file!?
+       git remote add svnsim testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump
+       git remote add svnfile testsvn::file://$TEST_DIRECTORY/t9154/svn.dump
+}
+
+if test -e "$GIT_BUILD_DIR/git-remote-testsvn"
+then
+       test_set_prereq REMOTE_SVN
+fi
+
+test_debug '
+       git --version
+       which git
+       which svnrdump
+'
+
+test_expect_success REMOTE_SVN 'simple fetch' '
+       init_git &&
+       git fetch svnsim &&
+       test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master  &&
+       cp .git/refs/remotes/svnsim/master master.good
+'
+
+test_debug '
+       cat .git/refs/svn/svnsim/master
+       cat .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'repeated fetch, nothing shall change' '
+       git fetch svnsim &&
+       test_cmp master.good .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'fetch from a file:// url gives the same result' '
+       git fetch svnfile
+'
+
+test_expect_failure REMOTE_SVN 'the sha1 differ because the git-svn-id line in the commit msg contains the url' '
+       test_cmp .git/refs/remotes/svnfile/master .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'mark-file regeneration' '
+       # filter out any other marks, that can not be regenerated. Only up to 3 digit revisions are allowed here
+       grep ":[0-9]\{1,3\} " $MARKSPATH/svnsim.marks > $MARKSPATH/svnsim.marks.old &&
+       rm $MARKSPATH/svnsim.marks &&
+       git fetch svnsim &&
+       test_cmp $MARKSPATH/svnsim.marks.old $MARKSPATH/svnsim.marks
+'
+
+test_expect_success REMOTE_SVN 'incremental imports must lead to the same head' '
+       export SVNRMAX=3 &&
+       init_git &&
+       git fetch svnsim &&
+       test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master  &&
+       unset SVNRMAX &&
+       git fetch svnsim &&
+       test_cmp master.good .git/refs/remotes/svnsim/master
+'
+
+test_debug 'git branch -a'
+
+test_done
index 514282cbdfde7e661dcf17c0e1388c3c03a8d287..489bc80fc1b1ba4066a1fd5ad6d2024d67160ba7 100644 (file)
@@ -230,7 +230,7 @@ else
        say_color() {
                test -z "$1" && test -n "$quiet" && return
                shift
-               echo "$*"
+               printf "%s\n" "$*"
        }
 fi
 
index 83633a21e586fd94015bd983b683ffc10b2c1963..0f2d9a4a3d8ea732c7c61e3818bd0bbe417c76b0 100644 (file)
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
        if (argc == 2) {
                if (svndump_init(argv[1]))
                        return 1;
-               svndump_read(NULL);
+               svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
                svndump_deinit();
                svndump_reset();
                return 0;
index cfe098849022fcc56bcb1a57d00e4c89e753492b..4713b6930248d961824c9f867b10340e6278132f 100644 (file)
@@ -10,6 +10,7 @@
 #include "string-list.h"
 #include "thread-utils.h"
 #include "sigchain.h"
+#include "argv-array.h"
 
 static int debug;
 
@@ -19,6 +20,7 @@ struct helper_data {
        FILE *out;
        unsigned fetch : 1,
                import : 1,
+               bidi_import : 1,
                export : 1,
                option : 1,
                push : 1,
@@ -101,6 +103,7 @@ static void do_take_over(struct transport *transport)
 static struct child_process *get_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
+       struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct child_process *helper;
        const char **refspecs = NULL;
@@ -122,11 +125,10 @@ static struct child_process *get_helper(struct transport *transport)
        helper->in = -1;
        helper->out = -1;
        helper->err = 0;
-       helper->argv = xcalloc(4, sizeof(*helper->argv));
-       strbuf_addf(&buf, "git-remote-%s", data->name);
-       helper->argv[0] = strbuf_detach(&buf, NULL);
-       helper->argv[1] = transport->remote->name;
-       helper->argv[2] = remove_ext_force(transport->url);
+       argv_array_pushf(&argv, "git-remote-%s", data->name);
+       argv_array_push(&argv, transport->remote->name);
+       argv_array_push(&argv, remove_ext_force(transport->url));
+       helper->argv = argv_array_detach(&argv, NULL);
        helper->git_cmd = 0;
        helper->silent_exec_failure = 1;
 
@@ -178,6 +180,8 @@ static struct child_process *get_helper(struct transport *transport)
                        data->push = 1;
                else if (!strcmp(capname, "import"))
                        data->import = 1;
+               else if (!strcmp(capname, "bidi-import"))
+                       data->bidi_import = 1;
                else if (!strcmp(capname, "export"))
                        data->export = 1;
                else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
@@ -241,8 +245,7 @@ static int disconnect_helper(struct transport *transport)
                close(data->helper->out);
                fclose(data->out);
                res = finish_command(data->helper);
-               free((char *)data->helper->argv[0]);
-               free(data->helper->argv);
+               argv_array_free_detached(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
@@ -376,14 +379,23 @@ static int fetch_with_fetch(struct transport *transport,
 static int get_importer(struct transport *transport, struct child_process *fastimport)
 {
        struct child_process *helper = get_helper(transport);
+       struct helper_data *data = transport->data;
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       int cat_blob_fd, code;
        memset(fastimport, 0, sizeof(*fastimport));
        fastimport->in = helper->out;
-       fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
-       fastimport->argv[0] = "fast-import";
-       fastimport->argv[1] = "--quiet";
+       argv_array_push(&argv, "fast-import");
+       argv_array_push(&argv, debug ? "--stats" : "--quiet");
 
+       if (data->bidi_import) {
+               cat_blob_fd = xdup(helper->in);
+               argv_array_pushf(&argv, "--cat-blob-fd=%d", cat_blob_fd);
+       }
+       fastimport->argv = argv.argv;
        fastimport->git_cmd = 1;
-       return start_command(fastimport);
+
+       code = start_command(fastimport);
+       return code;
 }
 
 static int get_exporter(struct transport *transport,
@@ -438,11 +450,17 @@ static int fetch_with_import(struct transport *transport,
        }
 
        write_constant(data->helper->in, "\n");
+       /*
+        * remote-helpers that advertise the bidi-import capability are required to
+        * buffer the complete batch of import commands until this newline before
+        * sending data to fast-import.
+        * These helpers read back data from fast-import on their stdin, which could
+        * be mixed with import commands, otherwise.
+        */
 
        if (finish_command(&fastimport))
                die("Error while running fast-import");
-       free(fastimport.argv);
-       fastimport.argv = NULL;
+       argv_array_free_detached(fastimport.argv);
 
        /*
         * The fast-import stream of a remote helper that advertises
index 2e90ccb74fe45e6335ee6a76451da40d7d3f5a09..6142421ea1172f7fe6b57486a4f41b81d92bab85 100644 (file)
@@ -727,12 +727,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
                " include-tag multi_ack_detailed";
        struct object *o = lookup_unknown_object(sha1);
        const char *refname_nons = strip_namespace(refname);
-
-       if (o->type == OBJ_NONE) {
-               o->type = sha1_object_info(sha1, NULL);
-               if (o->type < 0)
-                   die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
-       }
+       unsigned char peeled[20];
 
        if (capabilities)
                packet_write(1, "%s %s%c%s%s agent=%s\n",
@@ -747,11 +742,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
                o->flags |= OUR_REF;
                nr_our_refs++;
        }
-       if (o->type == OBJ_TAG) {
-               o = deref_tag_noverify(o);
-               if (o)
-                       packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
-       }
+       if (!peel_ref(refname, peeled))
+               packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
        return 0;
 }
 
index 1f04697866bd481e24f17e0833c1c2d29c8e12e2..f2b23c81de11a63c2c1c2ac925be9081ad5bea5c 100644 (file)
@@ -3,8 +3,7 @@
  * See LICENSE for details.
  */
 
-#include "git-compat-util.h"
-#include "strbuf.h"
+#include "cache.h"
 #include "quote.h"
 #include "fast_export.h"
 #include "repo_tree.h"
@@ -68,11 +67,33 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
        putchar('\n');
 }
 
+void fast_export_begin_note(uint32_t revision, const char *author,
+               const char *log, unsigned long timestamp, const char *note_ref)
+{
+       static int firstnote = 1;
+       size_t loglen = strlen(log);
+       printf("commit %s\n", note_ref);
+       printf("committer %s <%s@%s> %ld +0000\n", author, author, "local", timestamp);
+       printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
+       fwrite(log, loglen, 1, stdout);
+       if (firstnote) {
+               if (revision > 1)
+                       printf("from %s^0", note_ref);
+               firstnote = 0;
+       }
+       fputc('\n', stdout);
+}
+
+void fast_export_note(const char *committish, const char *dataref)
+{
+       printf("N %s %s\n", dataref, committish);
+}
+
 static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
                        const struct strbuf *log,
                        const char *uuid, const char *url,
-                       unsigned long timestamp)
+                       unsigned long timestamp, const char *local_ref)
 {
        static const struct strbuf empty = STRBUF_INIT;
        if (!log)
@@ -84,7 +105,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
        } else {
                *gitsvnline = '\0';
        }
-       printf("commit refs/heads/master\n");
+       printf("commit %s\n", local_ref);
        printf("mark :%"PRIu32"\n", revision);
        printf("committer %s <%s@%s> %ld +0000\n",
                   *author ? author : "nobody",
@@ -222,6 +243,13 @@ static long apply_delta(off_t len, struct line_buffer *input,
        return ret;
 }
 
+void fast_export_buf_to_data(const struct strbuf *data)
+{
+       printf("data %"PRIuMAX"\n", (uintmax_t)data->len);
+       fwrite(data->buf, data->len, 1, stdout);
+       fputc('\n', stdout);
+}
+
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
 {
        assert(len >= 0);
index 8823aca15c9592ce732e741e879aa182f2273202..c8b5adb811c7ba5652b94a6cf6f9ea54d6d9335f 100644 (file)
@@ -9,11 +9,15 @@ void fast_export_deinit(void);
 
 void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
+void fast_export_note(const char *committish, const char *dataref);
+void fast_export_begin_note(uint32_t revision, const char *author,
+               const char *log, unsigned long timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
-                       const struct strbuf *log, const char *uuid,
-                       const char *url, unsigned long timestamp);
+                       const struct strbuf *log, const char *uuid,const char *url,
+                       unsigned long timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
+void fast_export_buf_to_data(const struct strbuf *data);
 void fast_export_blob_delta(uint32_t mode,
                        uint32_t old_mode, const char *old_data,
                        off_t len, struct line_buffer *input);
index 2b168aee75ddc299f22c738b26f07e12c285f9ae..31d1d83d451e0e78f19067799d14e555a62e2cc8 100644 (file)
@@ -48,7 +48,7 @@ static struct {
 static struct {
        uint32_t revision;
        unsigned long timestamp;
-       struct strbuf log, author;
+       struct strbuf log, author, note;
 } rev_ctx;
 
 static struct {
@@ -77,6 +77,7 @@ static void reset_rev_ctx(uint32_t revision)
        rev_ctx.timestamp = 0;
        strbuf_reset(&rev_ctx.log);
        strbuf_reset(&rev_ctx.author);
+       strbuf_reset(&rev_ctx.note);
 }
 
 static void reset_dump_ctx(const char *url)
@@ -299,22 +300,29 @@ static void handle_node(void)
                                node_ctx.text_length, &input);
 }
 
-static void begin_revision(void)
+static void begin_revision(const char *remote_ref)
 {
        if (!rev_ctx.revision)  /* revision 0 gets no git commit. */
                return;
        fast_export_begin_commit(rev_ctx.revision, rev_ctx.author.buf,
                &rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
-               rev_ctx.timestamp);
+               rev_ctx.timestamp, remote_ref);
 }
 
-static void end_revision(void)
+static void end_revision(const char *note_ref)
 {
-       if (rev_ctx.revision)
+       struct strbuf mark = STRBUF_INIT;
+       if (rev_ctx.revision) {
                fast_export_end_commit(rev_ctx.revision);
+               fast_export_begin_note(rev_ctx.revision, "remote-svn",
+                               "Note created by remote-svn.", rev_ctx.timestamp, note_ref);
+               strbuf_addf(&mark, ":%"PRIu32, rev_ctx.revision);
+               fast_export_note(mark.buf, "inline");
+               fast_export_buf_to_data(&rev_ctx.note);
+       }
 }
 
-void svndump_read(const char *url)
+void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
 {
        char *val;
        char *t;
@@ -353,11 +361,12 @@ void svndump_read(const char *url)
                        if (active_ctx == NODE_CTX)
                                handle_node();
                        if (active_ctx == REV_CTX)
-                               begin_revision();
+                               begin_revision(local_ref);
                        if (active_ctx != DUMP_CTX)
-                               end_revision();
+                               end_revision(notes_ref);
                        active_ctx = REV_CTX;
                        reset_rev_ctx(atoi(val));
+                       strbuf_addf(&rev_ctx.note, "%s\n", t);
                        break;
                case sizeof("Node-path"):
                        if (constcmp(t, "Node-"))
@@ -366,13 +375,15 @@ void svndump_read(const char *url)
                                if (active_ctx == NODE_CTX)
                                        handle_node();
                                if (active_ctx == REV_CTX)
-                                       begin_revision();
+                                       begin_revision(local_ref);
                                active_ctx = NODE_CTX;
                                reset_node_ctx(val);
+                               strbuf_addf(&rev_ctx.note, "%s\n", t);
                                break;
                        }
                        if (constcmp(t + strlen("Node-"), "kind"))
                                continue;
+                       strbuf_addf(&rev_ctx.note, "%s\n", t);
                        if (!strcmp(val, "dir"))
                                node_ctx.type = REPO_MODE_DIR;
                        else if (!strcmp(val, "file"))
@@ -383,6 +394,7 @@ void svndump_read(const char *url)
                case sizeof("Node-action"):
                        if (constcmp(t, "Node-action"))
                                continue;
+                       strbuf_addf(&rev_ctx.note, "%s\n", t);
                        if (!strcmp(val, "delete")) {
                                node_ctx.action = NODEACT_DELETE;
                        } else if (!strcmp(val, "add")) {
@@ -401,11 +413,13 @@ void svndump_read(const char *url)
                                continue;
                        strbuf_reset(&node_ctx.src);
                        strbuf_addstr(&node_ctx.src, val);
+                       strbuf_addf(&rev_ctx.note, "%s\n", t);
                        break;
                case sizeof("Node-copyfrom-rev"):
                        if (constcmp(t, "Node-copyfrom-rev"))
                                continue;
                        node_ctx.srcRev = atoi(val);
+                       strbuf_addf(&rev_ctx.note, "%s\n", t);
                        break;
                case sizeof("Text-content-length"):
                        if (constcmp(t, "Text") && constcmp(t, "Prop"))
@@ -463,25 +477,40 @@ void svndump_read(const char *url)
        if (active_ctx == NODE_CTX)
                handle_node();
        if (active_ctx == REV_CTX)
-               begin_revision();
+               begin_revision(local_ref);
        if (active_ctx != DUMP_CTX)
-               end_revision();
+               end_revision(notes_ref);
 }
 
-int svndump_init(const char *filename)
+static void init(int report_fd)
 {
-       if (buffer_init(&input, filename))
-               return error("cannot open %s: %s", filename, strerror(errno));
-       fast_export_init(REPORT_FILENO);
+       fast_export_init(report_fd);
        strbuf_init(&dump_ctx.uuid, 4096);
        strbuf_init(&dump_ctx.url, 4096);
        strbuf_init(&rev_ctx.log, 4096);
        strbuf_init(&rev_ctx.author, 4096);
+       strbuf_init(&rev_ctx.note, 4096);
        strbuf_init(&node_ctx.src, 4096);
        strbuf_init(&node_ctx.dst, 4096);
        reset_dump_ctx(NULL);
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
+       return;
+}
+
+int svndump_init(const char *filename)
+{
+       if (buffer_init(&input, filename))
+               return error("cannot open %s: %s", filename ? filename : "NULL", strerror(errno));
+       init(REPORT_FILENO);
+       return 0;
+}
+
+int svndump_init_fd(int in_fd, int back_fd)
+{
+       if(buffer_fdinit(&input, xdup(in_fd)))
+               return error("cannot open fd %d: %s", in_fd, strerror(errno));
+       init(xdup(back_fd));
        return 0;
 }
 
@@ -492,6 +521,8 @@ void svndump_deinit(void)
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
        strbuf_release(&rev_ctx.log);
+       strbuf_release(&rev_ctx.author);
+       strbuf_release(&rev_ctx.note);
        strbuf_release(&node_ctx.src);
        strbuf_release(&node_ctx.dst);
        if (buffer_deinit(&input))
index df9ceb0e8d473b65fc22ac142d3856dc4f9980e0..b8eb12954e816e0e1490f6ef7eefe64901c38ae1 100644 (file)
@@ -2,7 +2,8 @@
 #define SVNDUMP_H_
 
 int svndump_init(const char *filename);
-void svndump_read(const char *url);
+int svndump_init_fd(int in_fd, int back_fd);
+void svndump_read(const char *url, const char *local_ref, const char *notes_ref);
 void svndump_deinit(void);
 void svndump_reset(void);
 
index d45b536021e15c5a674f7969be39f238194bef99..960f448cffd9ffd4e53763dfed3669bc374b8620 100644 (file)
@@ -34,12 +34,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
                        return;
        }
        if (fflush(f)) {
-               /*
-                * On Windows, EPIPE is returned only by the first write()
-                * after the reading end has closed its handle; subsequent
-                * write()s return EINVAL.
-                */
-               if (errno == EPIPE || errno == EINVAL)
+               if (errno == EPIPE)
                        exit(0);
                die_errno("write failure on '%s'", desc);
        }