Merge branch 'ap/path-max'
authorJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:32:18 +0000 (10:32 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:32:18 +0000 (10:32 -0800)
* ap/path-max:
Prevent buffer overflows when path is too long

158 files changed:
Documentation/RelNotes/1.8.5.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.9.txt
Documentation/git-fetch.txt
Documentation/git-pack-objects.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-replace.txt
Documentation/git.txt
Documentation/gitignore.txt
Documentation/gitmodules.txt
Documentation/gitweb.conf.txt
Documentation/glossary-content.txt
Documentation/merge-strategies.txt
abspath.c
alias.c
attr.c
bisect.c
branch.c
builtin/add.c
builtin/apply.c
builtin/archive.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/column.c
builtin/commit.c
builtin/describe.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/help.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge-recursive.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/push.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-ref.c
builtin/upload-archive.c
cache.h
commit.c
config.c
connect.c
connect.h
connected.c
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
contrib/remote-helpers/test-hg.sh
convert.c
csum-file.c
csum-file.h
daemon.c
diff-no-index.c
diff.c
diff.h
dir.c
environment.c
fast-import.c
fetch-pack.c
fetch-pack.h
gettext.c
git-compat-util.h
git-difftool--helper.sh
git-pull.sh
git-rebase.sh
git-send-email.perl
git.c
gitweb/gitweb.perl
help.c
http-backend.c
http-push.c
http.c
imap-send.c
log-tree.c
merge-recursive.c
notes-utils.c
notes.c
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
pathspec.c
pathspec.h
perl/Git/SVN/Editor.pm
pkt-line.c
po/de.po
pretty.c
refs.c
remote-curl.c
remote-testsvn.c
remote.c
remote.h
replace_object.c
revision.c
send-pack.c
sequencer.c
setup.c
sha1_file.c
sha1_name.c
shell.c
strbuf.c
streaming.c
submodule.c
t/perf/p4001-diff-no-index.sh [new file with mode: 0755]
t/t0002-gitfile.sh
t/t0003-attributes.sh
t/t1006-cat-file.sh
t/t1506-rev-parse-diagnosis.sh
t/t3400-rebase.sh
t/t4020-diff-external.sh
t/t4053-diff-no-index.sh
t/t5000-tar-tree.sh
t/t5302-pack-index.sh
t/t5500-fetch-pack.sh
t/t5516-fetch-push.sh
t/t5601-clone.sh
t/t6050-replace.sh
t/t6132-pathspec-exclude.sh [new file with mode: 0755]
t/t7001-mv.sh
t/t7507-commit-verbose.sh
tag.c
test-line-buffer.c
test-string-list.c
transport-helper.c
transport.c
tree-walk.c
upload-pack.c
vcs-svn/fast_export.c
wt-status.c
wt-status.h
diff --git a/Documentation/RelNotes/1.8.5.2.txt b/Documentation/RelNotes/1.8.5.2.txt
new file mode 100644 (file)
index 0000000..3ac4984
--- /dev/null
@@ -0,0 +1,20 @@
+Git v1.8.5.2 Release Notes
+==========================
+
+Fixes since v1.8.5.1
+--------------------
+
+ * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the
+   command line parser.
+
+ * "git cat-file --batch-check=ok" did not check the existence of
+   the named object.
+
+ * "git am --abort" sometimes complained about not being able to write
+   a tree with an 0{40} object in it.
+
+ * Two processes creating loose objects at the same time could have
+   failed unnecessarily when the name of their new objects started
+   with the same byte value, due to a race condition.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
index 7120c220e4758af837685707bdd7c52293b8f837..a966ab4887cdd6c34a1215ab588d3d5158826552 100644 (file)
@@ -84,6 +84,10 @@ Foreign interfaces, subsystems and ports.
 
 UI, Workflows & Features
 
+ * "gitweb" learned to treat ref hierarchies other than refs/heads as
+   if they are additional branch namespaces (e.g. refs/changes/ in
+   Gerrit).
+
  * "git for-each-ref --format=..." learned a few formatting directives;
    e.g. "%(color:red)%(HEAD)%(color:reset) %(refname:short) %(subject)".
 
@@ -105,6 +109,12 @@ UI, Workflows & Features
 
 Performance, Internal Implementation, etc.
 
+ * The deprecated parse-options macro OPT_BOOLEAN has been removed;
+   use OPT_BOOL or OPT_COUNTUP in new code.
+
+ * A few duplicate implementations of prefix/suffix string comparison
+   functions have been unified to starts_with() and ends_with().
+
  * The new PERLLIB_EXTRA makefile variable can be used to specify
    additional directories Perl modules (e.g. the ones necessary to run
    git-svn) are installed on the platform when building.
@@ -131,6 +141,33 @@ Unless otherwise noted, all the fixes since v1.8.5 in the maintenance
 track are contained in this release (see the maintenance releases' notes
 for details).
 
+ * "git mv A B/", when B does not exist as a directory, should error
+   out, but it didn't.
+   (merge c57f628 mm/mv-file-to-no-such-dir-with-slash later to maint).
+
+ * A workaround to an old bug in glibc prior to glibc 2.17 has been
+   retired; this would remove a side effect of the workaround that
+   corrupts system error messages in non-C locales.
+
+ * SSL-related options were not passed correctly to underlying socket
+   layer in "git send-email".
+   (merge 5508f3e tr/send-email-ssl later to maint).
+
+ * "git commit -v" appends the patch to the log message before
+   editing, and then removes the patch when the editor returned
+   control. However, the patch was not stripped correctly when the
+   first modified path was a submodule.
+   (merge 1a72cfd jl/commit-v-strip-marker later to maint).
+
+ * "git fetch --depth=0" was a no-op, and was silently ignored.
+   Diagnose it as an error.
+   (merge 5594bca nd/transport-positive-depth-only later to maint).
+
+ * Remote repository URL expressed in scp-style host:path notation are
+   parsed more carefully (e.g. "foo/bar:baz" is local, "[::1]:/~user" asks
+   to connect to user's home directory on host at address ::1.
+   (merge a2036d7 tb/clone-ssh-with-colon-for-port later to maint).
+
  * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the
    command line parser.
    (merge 887c6c1 nd/magic-pathspec later to maint).
index 10657134a80bd2cc013c34efeba0645ede368c64..5809aa4eb94e969bf700806b1ef3df504ebcfd2b 100644 (file)
@@ -37,6 +37,9 @@ or from several repositories at once if <group> is given and
 there is a remotes.<group> entry in the configuration file.
 (See linkgit:git-config[1]).
 
+When no remote is specified, by default the `origin` remote will be used,
+unless there's an upstream branch configured for the current branch.
+
 OPTIONS
 -------
 include::fetch-options.txt[]
index d94edcd4b4fdd8bd57150bd0ad8f870a45e0249c..cdab9ed503a5adaace14b06863f31347725d06db 100644 (file)
@@ -51,8 +51,7 @@ base-name::
        <base-name> to determine the name of the created file.
        When this option is used, the two files are written in
        <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
-       of the sorted object names to make the resulting filename
-       based on the pack content, and written to the standard
+       based on the pack content and is written to the standard
        output of the command.
 
 --stdout::
index 9eec74091097c0ea5c9fea69a09eb63e3cebd8d9..2b7f4f939f38626dbd796b808cc21285ad5c8c3d 100644 (file)
@@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or
 +
 The <dst> tells which ref on the remote side is updated with this
 push. Arbitrary expressions cannot be used here, an actual ref must
-be named. If `:`<dst> is omitted, the same ref as <src> will be
-updated.
+be named.
+If `git push [<repository>]` without any `<refspec>` argument is set to
+update some ref at the destination with `<src>` with
+`remote.<repository>.push` configuration variable, `:<dst>` part can
+be omitted---such a push will update a ref that `<src>` normally updates
+without any `<refspec>` on the command line.  Otherwise, missing
+`:<dst>` means to update the same ref as the `<src>`.
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side.  By default this is only allowed if <dst> is not
index 94e07fdab550dcac70734fb79724b00cf8d23edc..2889be6bdc4a1a243585e43def2e7416a9aa7038 100644 (file)
@@ -324,6 +324,16 @@ fresh commits so it can be remerged successfully without needing to "revert
 the reversion" (see the
 link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
 
+--fork-point::
+--no-fork-point::
+       Use 'git merge-base --fork-point' to find a better common ancestor
+       between `upstream` and `branch` when calculating which commits have
+       have been introduced by `branch` (see linkgit:git-merge-base[1]).
++
+If no non-option arguments are given on the command line, then the default is
+`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally
+unless the `--fork-point` option is specified.
+
 --ignore-whitespace::
 --whitespace=<option>::
        These flag are passed to the 'git apply' program
index f373ab48d4d575d847408e333b5bfb747d1a75a2..0a02f70657776bc5d862add34f466b4fe98f6e35 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git replace' [-f] <object> <replacement>
 'git replace' -d <object>...
-'git replace' -l [<pattern>]
+'git replace' [--format=<format>] [-l [<pattern>]]
 
 DESCRIPTION
 -----------
@@ -70,6 +70,23 @@ OPTIONS
        Typing "git replace" without arguments, also lists all replace
        refs.
 
+--format=<format>::
+       When listing, use the specified <format>, which can be one of
+       'short', 'medium' and 'long'. When omitted, the format
+       defaults to 'short'.
+
+FORMATS
+-------
+
+The following format are available:
+
+* 'short':
+       <replaced sha1>
+* 'medium':
+       <replaced sha1> -> <replacement sha1>
+* 'long':
+       <replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
+
 CREATING REPLACEMENT OBJECTS
 ----------------------------
 
index 4448ce2704284bde15a8a3222545993035e56467..aec372646ffc30f13b924159d47ade373fc44b87 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.5.1/git.html[documentation for release 1.8.5.1]
+* link:v1.8.5.2/git.html[documentation for release 1.8.5.2]
 
 * release notes for
+  link:RelNotes/1.8.5.2.txt[1.8.5.2],
   link:RelNotes/1.8.5.1.txt[1.8.5.1],
   link:RelNotes/1.8.5.txt[1.8.5].
 
@@ -806,6 +807,15 @@ temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
 +
 For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
 parameter, <path>.
++
+For each path 'GIT_EXTERNAL_DIFF' is called, two environment variables,
+'GIT_DIFF_PATH_COUNTER' and 'GIT_DIFF_PATH_TOTAL' are set.
+
+'GIT_DIFF_PATH_COUNTER'::
+       A 1-based counter incremented by one for every path.
+
+'GIT_DIFF_PATH_TOTAL'::
+       The total number of paths.
 
 other
 ~~~~~
index f9719605123003c97bdf2999f912712536fa927a..205e80ef884a8c8852d7e83ca1ee660a5e1e8414 100644 (file)
@@ -79,8 +79,10 @@ PATTERN FORMAT
 
  - An optional prefix "`!`" which negates the pattern; any
    matching file excluded by a previous pattern will become
-   included again.  If a negated pattern matches, this will
-   override lower precedence patterns sources.
+   included again. It is not possible to re-include a file if a parent
+   directory of that file is excluded. Git doesn't list excluded
+   directories for performance reasons, so any patterns on contained
+   files have no effect, no matter where they are defined.
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
@@ -182,6 +184,19 @@ Another example:
 The second .gitignore prevents Git from ignoring
 `arch/foo/kernel/vmlinux.lds.S`.
 
+Example to exclude everything except a specific directory `foo/bar`
+(note the `/*` - without the slash, the wildcard would also exclude
+everything within `foo/bar`):
+
+--------------------------------------------------------------
+    $ cat .gitignore
+    # exclude everything except directory foo/bar
+    /*
+    !/foo
+    /foo/*
+    !/foo/bar
+--------------------------------------------------------------
+
 SEE ALSO
 --------
 linkgit:git-rm[1],
index f7be93f6317c7170af642c43d107a6617eb33a18..347a9f76ee809c3691dc6580b0fba3ebde0b0e3c 100644 (file)
@@ -35,6 +35,8 @@ submodule.<name>.url::
        linkgit:git-clone[1] or (if it begins with ./ or ../) a location
        relative to the superproject's origin repository.
 
+In addition, there are a number of optional keys:
+
 submodule.<name>.update::
        Defines what to do when the submodule is updated by the superproject.
        If 'checkout' (the default), the new commit specified in the
index e2113d93c9d34aa0af3fd421d5f6ed9bf01ad896..db4154f9d5c84bfb1dc4453f3c3d5be1a525fc02 100644 (file)
@@ -849,6 +849,43 @@ time zones in the form of "+/-HHMM", such as "+0200".
 +
 Project specific override is not supported.
 
+extra-branch-refs::
+       List of additional directories under "refs" which are going to
+       be used as branch refs. For example if you have a gerrit setup
+       where all branches under refs/heads/ are official,
+       push-after-review ones and branches under refs/sandbox/,
+       refs/wip and refs/other are user ones where permissions are
+       much wider, then you might want to set this variable as
+       follows:
++
+--------------------------------------------------------------------------------
+$feature{'extra-branch-refs'}{'default'} =
+       ['sandbox', 'wip', 'other'];
+--------------------------------------------------------------------------------
++
+This feature can be configured on per-repository basis after setting
+$feature{'extra-branch-refs'}{'override'} to true, via repository's
+`gitweb.extraBranchRefs` configuration variable, which contains a
+space separated list of refs. An example:
++
+--------------------------------------------------------------------------------
+[gitweb]
+       extraBranchRefs = sandbox wip other
+--------------------------------------------------------------------------------
++
+The gitweb.extraBranchRefs is actually a multi-valued configuration
+variable, so following example is also correct and the result is the
+same as of the snippet above:
++
+--------------------------------------------------------------------------------
+[gitweb]
+       extraBranchRefs = sandbox
+       extraBranchRefs = wip other
+--------------------------------------------------------------------------------
++
+It is an error to specify a ref that does not pass "git check-ref-format"
+scrutiny. Duplicated values are filtered.
+
 
 EXAMPLES
 --------
index aa1c8880dd491a5840492d610c44fec6e07684ca..378306f58162fdfee900b48b8bb6ad8171cbfca3 100644 (file)
@@ -323,24 +323,26 @@ including Documentation/chapter_1/figure_1.jpg.
 A pathspec that begins with a colon `:` has special meaning.  In the
 short form, the leading colon `:` is followed by zero or more "magic
 signature" letters (which optionally is terminated by another colon `:`),
-and the remainder is the pattern to match against the path. The optional
-colon that terminates the "magic signature" can be omitted if the pattern
-begins with a character that cannot be a "magic signature" and is not a
-colon.
+and the remainder is the pattern to match against the path.
+The "magic signature" consists of ASCII symbols that are neither
+alphanumeric, glob, regex special charaters nor colon.
+The optional colon that terminates the "magic signature" can be
+omitted if the pattern begins with a character that does not belong to
+"magic signature" symbol set and is not a colon.
 +
 In the long form, the leading colon `:` is followed by a open
 parenthesis `(`, a comma-separated list of zero or more "magic words",
 and a close parentheses `)`, and the remainder is the pattern to match
 against the path.
 +
-The "magic signature" consists of an ASCII symbol that is not
-alphanumeric.
+A pathspec with only a colon means "there is no pathspec". This form
+should not be combined with other pathspec.
 +
 --
-top `/`;;
-       The magic word `top` (mnemonic: `/`) makes the pattern match
-       from the root of the working tree, even when you are running
-       the command from inside a subdirectory.
+top;;
+       The magic word `top` (magic signature: `/`) makes the pattern
+       match from the root of the working tree, even when you are
+       running the command from inside a subdirectory.
 
 literal;;
        Wildcards in the pattern such as `*` or `?` are treated
@@ -377,14 +379,12 @@ full pathname may have special meaning:
  - Other consecutive asterisks are considered invalid.
 +
 Glob magic is incompatible with literal magic.
+
+exclude;;
+       After a path matches any non-exclude pathspec, it will be run
+       through all exclude pathspec (magic signature: `!`). If it
+       matches, the path is ignored.
 --
-+
-Currently only the slash `/` is recognized as the "magic signature",
-but it is envisioned that we will support more types of magic in later
-versions of Git.
-+
-A pathspec with only a colon means "there is no pathspec". This form
-should not be combined with other pathspec.
 
 [[def_parent]]parent::
        A <<def_commit_object,commit object>> contains a (possibly empty) list
index 49a9a7d53f5836ecf2c5bcadb30f0d314414afe0..fb6e593e7c6f287612f30be6206c0492d77e38d3 100644 (file)
@@ -113,3 +113,11 @@ subtree::
        match the tree structure of A, instead of reading the trees at
        the same level. This adjustment is also done to the common
        ancestor tree.
+
+With the strategies that use 3-way merge (including the default, 'recursive'),
+if a change is made on both branches, but later reverted on one of the
+branches, that change will be present in the merged result; some people find
+this behavior confusing.  It occurs because only the heads and the merge base
+are considered when performing a merge, not the individual commits.  The merge
+algorithm therefore considers the reverted change as no change at all, and
+substitutes the changed version instead.
index 9c908e395b3456c6c0ecf15dabe37fcac0abca20..ca33558a91c5259a793fc56b571fa683e16b134f 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -143,7 +143,7 @@ static const char *real_path_internal(const char *path, int die_on_error)
 error_out:
        free(last_elem);
        if (*cwd && chdir(cwd))
-               die_errno ("Could not change back to '%s'", cwd);
+               die_errno("Could not change back to '%s'", cwd);
 
        return retval;
 }
diff --git a/alias.c b/alias.c
index 9938f03c255029ee8f5b10623e5e7bb65f2d7921..5efc3d69866731d0f56464c522b00f253f84c187 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -5,7 +5,7 @@ static char *alias_val;
 
 static int alias_lookup_cb(const char *k, const char *v, void *cb)
 {
-       if (!prefixcmp(k, "alias.") && !strcmp(k + 6, alias_key)) {
+       if (starts_with(k, "alias.") && !strcmp(k + 6, alias_key)) {
                if (!v)
                        return config_error_nonbool(k);
                alias_val = xstrdup(v);
diff --git a/attr.c b/attr.c
index 0e774c6f22f9fd6c7e27490e5bed5be1f9f10de5..8d13d70e80d6d52794d53ed071b8800f9c905a05 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -211,7 +211,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
        name = cp;
        namelen = strcspn(name, blank);
        if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
-           !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) {
+           starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
                if (!macro_ok) {
                        fprintf(stderr, "%s not allowed: %s:%d\n",
                                name, src, lineno);
index 1e46a4f50e16f059fa768cecc4c8331a88997021..37200b41f1dce3359c491039aeebea0108597833 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -406,9 +406,9 @@ static int register_ref(const char *refname, const unsigned char *sha1,
        if (!strcmp(refname, "bad")) {
                current_bad_sha1 = xmalloc(20);
                hashcpy(current_bad_sha1, sha1);
-       } else if (!prefixcmp(refname, "good-")) {
+       } else if (starts_with(refname, "good-")) {
                sha1_array_append(&good_revs, sha1);
-       } else if (!prefixcmp(refname, "skip-")) {
+       } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, sha1);
        }
 
index 9e6c68edaf90f27cf447ad1522da6d7de7827141..723a36bc540e41fe30b7b4fc2c8e893c4744ef3a 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -50,7 +50,7 @@ static int should_setup_rebase(const char *origin)
 void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
 {
        const char *shortname = remote + 11;
-       int remote_is_branch = !prefixcmp(remote, "refs/heads/");
+       int remote_is_branch = starts_with(remote, "refs/heads/");
        struct strbuf key = STRBUF_INIT;
        int rebasing = should_setup_rebase(origin);
 
@@ -272,7 +272,7 @@ void create_branch(const char *head,
                break;
        case 1:
                /* Unique completion -- good, only if it is a real branch */
-               if (prefixcmp(real_ref, "refs/heads/") &&
+               if (!starts_with(real_ref, "refs/heads/") &&
                    validate_remote_tracking_branch(real_ref)) {
                        if (explicit_tracking)
                                die(_(upstream_not_branch), start_name);
index 226f758869358444ad1c39e5e0a05c961fb76859..0df73ae73596546966af18e7076d9a6f54964786 100644 (file)
@@ -540,10 +540,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                               PATHSPEC_FROMTOP |
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
-                              PATHSPEC_ICASE);
+                              PATHSPEC_ICASE |
+                              PATHSPEC_EXCLUDE);
 
                for (i = 0; i < pathspec.nr; i++) {
                        const char *path = pathspec.items[i].match;
+                       if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+                               continue;
                        if (!seen[i] &&
                            ((pathspec.items[i].magic &
                              (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
index ef32e4f62415956aa8e2366589d4cd6200d3bf37..b0d0986226ccb7fa06b08bacba6269b8834e8dd6 100644 (file)
@@ -1409,10 +1409,10 @@ static void recount_diff(const char *line, int size, struct fragment *fragment)
                case '\\':
                        continue;
                case '@':
-                       ret = size < 3 || prefixcmp(line, "@@ ");
+                       ret = size < 3 || !starts_with(line, "@@ ");
                        break;
                case 'd':
-                       ret = size < 5 || prefixcmp(line, "diff ");
+                       ret = size < 5 || !starts_with(line, "diff ");
                        break;
                default:
                        ret = -1;
@@ -1798,11 +1798,11 @@ static struct fragment *parse_binary_hunk(char **buf_p,
 
        *status_p = 0;
 
-       if (!prefixcmp(buffer, "delta ")) {
+       if (starts_with(buffer, "delta ")) {
                patch_method = BINARY_DELTA_DEFLATED;
                origlen = strtoul(buffer + 6, NULL, 10);
        }
-       else if (!prefixcmp(buffer, "literal ")) {
+       else if (starts_with(buffer, "literal ")) {
                patch_method = BINARY_LITERAL_DEFLATED;
                origlen = strtoul(buffer + 8, NULL, 10);
        }
@@ -3627,12 +3627,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20
            hunk->oldpos == 1 && hunk->oldlines == 1 &&
            /* does preimage begin with the heading? */
            (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
-           !prefixcmp(++preimage, heading) &&
+           starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
            preimage[sizeof(heading) + 40 - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
-           !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+           starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
                return 0; /* it all looks fine */
 
        /* we may have full object name on the index line */
index 49178f159e246c9a99805d806fa5f096337e8d2f..a1e3b940c25ac20927d4daefa417d5b626e38a24 100644 (file)
@@ -57,9 +57,9 @@ static int run_remote_archiver(int argc, const char **argv,
        if (!buf)
                die(_("git archive: expected ACK/NAK, got EOF"));
        if (strcmp(buf, "ACK")) {
-               if (!prefixcmp(buf, "NACK "))
+               if (starts_with(buf, "NACK "))
                        die(_("git archive: NACK %s"), buf + 5);
-               if (!prefixcmp(buf, "ERR "))
+               if (starts_with(buf, "ERR "))
                        die(_("remote error: %s"), buf + 4);
                die(_("git archive: protocol error"));
        }
index 636a16ea4e3af71aef80ba42639f0933c286f485..b4d771673e7e9dccacbae8b29f4f1cd6a835151f 100644 (file)
@@ -81,13 +81,13 @@ static int parse_branch_color_slot(const char *var, int ofs)
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "branch", &colopts);
        if (!strcmp(var, "color.branch")) {
                branch_use_color = git_config_colorbool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.branch.")) {
+       if (starts_with(var, "color.branch.")) {
                int slot = parse_branch_color_slot(var, 13);
                if (slot < 0)
                        return 0;
@@ -868,7 +868,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (!strcmp(head, "HEAD")) {
                detached = 1;
        } else {
-               if (prefixcmp(head, "refs/heads/"))
+               if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
                head += 11;
        }
index b2ca775a80f54fcceba344d5545fe3cb34ba0184..f12071adc4079c9bbea4a43a89b0b3bc6a4bbc48 100644 (file)
@@ -193,25 +193,28 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
        return end - start + 1;
 }
 
-static void print_object_or_die(int fd, const unsigned char *sha1,
-                               enum object_type type, unsigned long size)
+static void print_object_or_die(int fd, struct expand_data *data)
 {
-       if (type == OBJ_BLOB) {
+       const unsigned char *sha1 = data->sha1;
+
+       assert(data->info.typep);
+
+       if (data->type == OBJ_BLOB) {
                if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
                        die("unable to stream %s to stdout", sha1_to_hex(sha1));
        }
        else {
-               enum object_type rtype;
-               unsigned long rsize;
+               enum object_type type;
+               unsigned long size;
                void *contents;
 
-               contents = read_sha1_file(sha1, &rtype, &rsize);
+               contents = read_sha1_file(sha1, &type, &size);
                if (!contents)
                        die("object %s disappeared", sha1_to_hex(sha1));
-               if (rtype != type)
+               if (type != data->type)
                        die("object %s changed type!?", sha1_to_hex(sha1));
-               if (rsize != size)
-                       die("object %s change size!?", sha1_to_hex(sha1));
+               if (data->info.sizep && size != data->size)
+                       die("object %s changed size!?", sha1_to_hex(sha1));
 
                write_or_die(fd, contents, size);
                free(contents);
@@ -238,7 +241,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
                return 0;
        }
 
-       if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
+       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
                return 0;
@@ -250,7 +253,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
        strbuf_release(&buf);
 
        if (opt->print_contents) {
-               print_object_or_die(1, data->sha1, data->type, data->size);
+               print_object_or_die(1, data);
                write_or_die(1, "\n", 1);
        }
        return 0;
@@ -274,6 +277,13 @@ static int batch_objects(struct batch_options *opt)
        strbuf_expand(&buf, opt->format, expand_format, &data);
        data.mark_query = 0;
 
+       /*
+        * If we are printing out the object, then always fill in the type,
+        * since we will want to decide whether or not to stream.
+        */
+       if (opt->print_contents)
+               data.info.typep = &data.type;
+
        /*
         * We are going to call get_sha1 on a potentially very large number of
         * objects. In most large cases, these will be actual object sha1s. The
index 904fd715f0f27af0ac3694209ba3ffe61f174fa8..5df3837e3102e2f7d432ec11bb4685535bfaa0ac 100644 (file)
@@ -781,7 +781,7 @@ static int switch_branches(const struct checkout_opts *opts,
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
-       if (old.path && !prefixcmp(old.path, "refs/heads/"))
+       if (old.path && starts_with(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
 
        if (!new->name) {
@@ -816,7 +816,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
 
        return git_xmerge_config(var, value, NULL);
@@ -1151,9 +1151,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
                        die (_("--track needs a branch name"));
-               if (!prefixcmp(argv0, "refs/"))
+               if (starts_with(argv0, "refs/"))
                        argv0 += 5;
-               if (!prefixcmp(argv0, "remotes/"))
+               if (starts_with(argv0, "remotes/"))
                        argv0 += 8;
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
index 615cd57caf1d4cbeafc73e7037bae81a6915df59..2f26297142fde89ec842d4e01665b3ebb24a9b59 100644 (file)
@@ -100,7 +100,7 @@ static int parse_clean_color_slot(const char *var)
 
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "clean", &colopts);
 
        /* honors the color.interactive* config variables which also
@@ -109,7 +109,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
                clean_use_color = git_config_colorbool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.interactive.")) {
+       if (starts_with(var, "color.interactive.")) {
                int slot = parse_clean_color_slot(var +
                                                  strlen("color.interactive."));
                if (slot < 0)
index 874e0fd0b6e3ea4882783c0b2377016d93bd989d..f98f52980d3b8f7a4700d37c486c4379a4dd15ed 100644 (file)
@@ -508,9 +508,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
 {
        const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue;
-               if (!suffixcmp(ref->name, "^{}"))
+               if (ends_with(ref->name, "^{}"))
                        continue;
                if (!has_sha1_file(ref->old_sha1))
                        continue;
@@ -578,7 +578,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
                        const char *msg)
 {
-       if (our && !prefixcmp(our->name, "refs/heads/")) {
+       if (our && starts_with(our->name, "refs/heads/")) {
                /* Local default branch link */
                create_symref("HEAD", our->name, NULL);
                if (!option_bare) {
@@ -625,7 +625,7 @@ static int checkout(void)
                if (advice_detached_head)
                        detach_advice(sha1_to_hex(sha1));
        } else {
-               if (prefixcmp(head, "refs/heads/"))
+               if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
        }
        free(head);
@@ -796,6 +796,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_local > 0 && !is_local)
                warning(_("--local is ignored"));
 
+       /* no need to be strict, transport_set_option() will validate it again */
+       if (option_depth && atoi(option_depth) < 1)
+               die(_("depth %s is not a positive number"), option_depth);
+
        if (argc == 2)
                dir = xstrdup(argv[1]);
        else
index e125a55fc9de2b8f6d82fd5574f109b3d245934c..75818520e1b74de607620e48d2d3afb131c6e896 100644 (file)
@@ -34,7 +34,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
        };
 
        /* This one is special and must be the first one */
-       if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+       if (argc > 1 && starts_with(argv[1], "--command=")) {
                command = argv[1] + 10;
                git_config(column_config, (void *)command);
        } else
index e89c519192121d5386ba42b88a0157b31d57c690..3767478c6ddb02cf8a4a418f8911102aeaacfbe8 100644 (file)
@@ -733,7 +733,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                eol = nl - sb.buf;
                        else
                                eol = sb.len;
-                       if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+                       if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
                                ignore_footer = sb.len - previous;
                                break;
                        }
@@ -904,7 +904,7 @@ static int rest_is_empty(struct strbuf *sb, int start)
                        eol = sb->len;
 
                if (strlen(sign_off_header) <= eol - i &&
-                   !prefixcmp(sb->buf + i, sign_off_header)) {
+                   starts_with(sb->buf + i, sign_off_header)) {
                        i = eol;
                        continue;
                }
@@ -1183,7 +1183,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 {
        struct wt_status *s = cb;
 
-       if (!prefixcmp(k, "column."))
+       if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
@@ -1211,7 +1211,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
-       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+       if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
                        return 0;
@@ -1377,7 +1377,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
 
        head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
-               !prefixcmp(head, "refs/heads/") ?
+               starts_with(head, "refs/heads/") ?
                        head + 11 :
                        !strcmp(head, "HEAD") ?
                                _("detached HEAD") :
@@ -1505,7 +1505,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
-       char *nl, *p;
+       char *nl;
        unsigned char sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
@@ -1601,11 +1601,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        /* Truncate the message just before the diff, if any. */
-       if (verbose) {
-               p = strstr(sb.buf, "\ndiff --git ");
-               if (p != NULL)
-                       strbuf_setlen(&sb, p - sb.buf + 1);
-       }
+       if (verbose)
+               wt_status_truncate_message_at_cut_line(&sb);
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
index 6f6210988709dc866bf79a5bb5523356773b93a0..7db43dae1be95e86c21249d9150fc8553c367527 100644 (file)
@@ -141,7 +141,7 @@ static void add_to_known_names(const char *path,
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       int is_tag = !prefixcmp(path, "refs/tags/");
+       int is_tag = starts_with(path, "refs/tags/");
        unsigned char peeled[20];
        int is_annotated, prio;
 
index fe0cc7f1b5b1451f267b1d9e24a6e40cd6e55325..0f247d24008a4dc7ba5958e716c13f7582a820f1 100644 (file)
@@ -16,6 +16,9 @@
 #include "submodule.h"
 #include "sha1-array.h"
 
+#define DIFF_NO_INDEX_EXPLICIT 1
+#define DIFF_NO_INDEX_IMPLICIT 2
+
 struct blobinfo {
        unsigned char sha1[20];
        const char *name;
@@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
        struct blobinfo blob[2];
-       int nongit;
+       int nongit = 0, no_index = 0;
        int result = 0;
 
        /*
@@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         * Other cases are errors.
         */
 
-       prefix = setup_git_directory_gently(&nongit);
-       gitmodules_config();
+       /* Were we asked to do --no-index explicitly? */
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(argv[i], "--no-index"))
+                       no_index = DIFF_NO_INDEX_EXPLICIT;
+               if (argv[i][0] != '-')
+                       break;
+       }
+
+       if (!no_index)
+               prefix = setup_git_directory_gently(&nongit);
+
+       /*
+        * Treat git diff with at least one path outside of the
+        * repo the same as if the command would have been executed
+        * outside of a git repository.  In this case it behaves
+        * the same way as "git diff --no-index <a> <b>", which acts
+        * as a colourful "diff" replacement.
+        */
+       if (nongit || ((argc == i + 2) &&
+                      (!path_inside_repo(prefix, argv[i]) ||
+                       !path_inside_repo(prefix, argv[i + 1]))))
+               no_index = DIFF_NO_INDEX_IMPLICIT;
+
+       if (!no_index)
+               gitmodules_config();
        git_config(git_diff_ui_config, NULL);
 
        init_revisions(&rev, prefix);
 
-       /* If this is a no-index diff, just run it and exit there. */
-       diff_no_index(&rev, argc, argv, nongit, prefix);
+       if (no_index && argc != i + 2) {
+               if (no_index == DIFF_NO_INDEX_IMPLICIT) {
+                       /*
+                        * There was no --no-index and there were not two
+                        * paths. It is possible that the user intended
+                        * to do an inside-repository operation.
+                        */
+                       fprintf(stderr, "Not a git repository\n");
+                       fprintf(stderr,
+                               "To compare two paths outside a working tree:\n");
+               }
+               /* Give the usage message for non-repository usage and exit. */
+               usagef("git diff %s <path> <path>",
+                      no_index == DIFF_NO_INDEX_EXPLICIT ?
+                      "--no-index" : "[--no-index]");
+
+       }
+       if (no_index)
+               /* If this is a no-index diff, just run it and exit there. */
+               diff_no_index(&rev, argc, argv, prefix);
 
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
index ea6305258d4d63e58f2453736cfc5aa38ec37282..b8d8a3aaf9ee8294be9a78956a70b476d327ee05 100644 (file)
@@ -476,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag)
                }
        }
 
-       if (!prefixcmp(name, "refs/tags/"))
+       if (starts_with(name, "refs/tags/"))
                name += 10;
        printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
               name, tagged_mark,
index c8e858232a8e7a3536ef4d296efdc618e034c36e..8b8978a25287bce6c4f937ff52df6cb7ae8e429e 100644 (file)
@@ -7,7 +7,7 @@
 static const char fetch_pack_usage[] =
 "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
 "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
-"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
 
 static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
                                 const char *name, int namelen)
@@ -48,11 +48,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc && *argv[i] == '-'; i++) {
                const char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--upload-pack=")) {
+               if (starts_with(arg, "--upload-pack=")) {
                        args.uploadpack = arg + 14;
                        continue;
                }
-               if (!prefixcmp(arg, "--exec=")) {
+               if (starts_with(arg, "--exec=")) {
                        args.uploadpack = arg + 7;
                        continue;
                }
@@ -81,11 +81,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        args.stdin_refs = 1;
                        continue;
                }
+               if (!strcmp("--diag-url", arg)) {
+                       args.diag_url = 1;
+                       continue;
+               }
                if (!strcmp("-v", arg)) {
                        args.verbose = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--depth=")) {
+               if (starts_with(arg, "--depth=")) {
                        args.depth = strtol(arg + 8, NULL, 0);
                        continue;
                }
@@ -146,10 +150,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
+               int flags = args.verbose ? CONNECT_VERBOSE : 0;
+               if (args.diag_url)
+                       flags |= CONNECT_DIAG_URL;
                conn = git_connect(fd, dest, args.uploadpack,
-                                  args.verbose ? CONNECT_VERBOSE : 0);
+                                  flags);
+               if (!conn)
+                       return args.diag_url ? 0 : 1;
        }
-
        get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
 
        ref = fetch_pack(&args, fd, conn, ref, dest,
index 3d978eb58ec5cc1e2629db10562319c666bc031e..1e7d617f4671c43f704afe912edac5f5dc6dc316 100644 (file)
@@ -192,7 +192,7 @@ static void find_non_local_tags(struct transport *transport,
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue;
 
                /*
@@ -201,7 +201,7 @@ static void find_non_local_tags(struct transport *transport,
                 * to fetch then we can mark the ref entry in the list
                 * as one to ignore by setting util to NULL.
                 */
-               if (!suffixcmp(ref->name, "^{}")) {
+               if (ends_with(ref->name, "^{}")) {
                        if (item && !has_sha1_file(ref->old_sha1) &&
                            !will_fetch(head, ref->old_sha1) &&
                            !has_sha1_file(item->util) &&
@@ -431,7 +431,7 @@ static int update_local_ref(struct ref *ref,
        }
 
        if (!is_null_sha1(ref->old_sha1) &&
-           !prefixcmp(ref->name, "refs/tags/")) {
+           starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
@@ -454,10 +454,10 @@ static int update_local_ref(struct ref *ref,
                 * more likely to follow a standard layout.
                 */
                const char *name = remote_ref ? remote_ref->name : "";
-               if (!prefixcmp(name, "refs/tags/")) {
+               if (starts_with(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
-               } else if (!prefixcmp(name, "refs/heads/")) {
+               } else if (starts_with(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
                } else {
@@ -589,15 +589,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                kind = "";
                                what = "";
                        }
-                       else if (!prefixcmp(rm->name, "refs/heads/")) {
+                       else if (starts_with(rm->name, "refs/heads/")) {
                                kind = "branch";
                                what = rm->name + 11;
                        }
-                       else if (!prefixcmp(rm->name, "refs/tags/")) {
+                       else if (starts_with(rm->name, "refs/tags/")) {
                                kind = "tag";
                                what = rm->name + 10;
                        }
-                       else if (!prefixcmp(rm->name, "refs/remotes/")) {
+                       else if (starts_with(rm->name, "refs/remotes/")) {
                                kind = "remote-tracking branch";
                                what = rm->name + 13;
                        }
@@ -896,7 +896,7 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 {
        struct remote_group_data *g = priv;
 
-       if (!prefixcmp(key, "remotes.") &&
+       if (starts_with(key, "remotes.") &&
                        !strcmp(key + 8, g->name)) {
                /* split list by white space */
                int space = strcspn(value, " \t\n");
@@ -1079,6 +1079,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                }
        }
 
+       /* no need to be strict, transport_set_option() will validate it again */
+       if (depth && atoi(depth) < 1)
+               die(_("depth %s is not a positive number"), depth);
+
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
                        int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
index 1c040708696c8d6e81023b3261966fa751f14ad4..3906eda87712ea099e0e5876fff1fe28fc5716be 100644 (file)
@@ -109,7 +109,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        if (len < 43 || line[40] != '\t')
                return 1;
 
-       if (!prefixcmp(line + 41, "not-for-merge"))
+       if (starts_with(line + 41, "not-for-merge"))
                return 0;
 
        if (line[41] != '\t')
@@ -155,16 +155,16 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        if (pulling_head) {
                origin = src;
                src_data->head_status |= 1;
-       } else if (!prefixcmp(line, "branch ")) {
+       } else if (starts_with(line, "branch ")) {
                origin_data->is_local_branch = 1;
                origin = line + 7;
                string_list_append(&src_data->branch, origin);
                src_data->head_status |= 2;
-       } else if (!prefixcmp(line, "tag ")) {
+       } else if (starts_with(line, "tag ")) {
                origin = line;
                string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
-       } else if (!prefixcmp(line, "remote-tracking branch ")) {
+       } else if (starts_with(line, "remote-tracking branch ")) {
                origin = line + strlen("remote-tracking branch ");
                string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
@@ -605,7 +605,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                resolve_refdup("HEAD", head_sha1, 1, NULL);
        if (!current_branch)
                die("No current branch");
-       if (!prefixcmp(current_branch, "refs/heads/"))
+       if (starts_with(current_branch, "refs/heads/"))
                current_branch += 11;
 
        find_merge_parents(&merge_parents, in, head_sha1);
index 875bd813d5ed6d09622fc3473fdb574e9714bf7f..51798b48b54266fccd2f7b4687646d152ab70588 100644 (file)
@@ -92,7 +92,7 @@ static struct {
  */
 static const char **used_atom;
 static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
+static int used_atom_cnt, need_tagged, need_symref;
 static int need_color_reset_at_eol;
 
 /*
@@ -453,7 +453,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (name[wholen] != 0 &&
                    strcmp(name + wholen, "name") &&
                    strcmp(name + wholen, "email") &&
-                   prefixcmp(name + wholen, "date"))
+                   !starts_with(name + wholen, "date"))
                        continue;
                if (!wholine)
                        wholine = find_wholine(who, wholen, buf, sz);
@@ -465,7 +465,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                        v->s = copy_name(wholine);
                else if (!strcmp(name + wholen, "email"))
                        v->s = copy_email(wholine);
-               else if (!prefixcmp(name + wholen, "date"))
+               else if (starts_with(name + wholen, "date"))
                        grab_date(wholine, v, name);
        }
 
@@ -487,7 +487,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (deref)
                        name++;
 
-               if (!prefixcmp(name, "creatordate"))
+               if (starts_with(name, "creatordate"))
                        grab_date(wholine, v, name);
                else if (!strcmp(name, "creator"))
                        v->s = copy_line(wholine);
@@ -668,13 +668,13 @@ static void populate_value(struct refinfo *ref)
                        name++;
                }
 
-               if (!prefixcmp(name, "refname"))
+               if (starts_with(name, "refname"))
                        refname = ref->refname;
-               else if (!prefixcmp(name, "symref"))
+               else if (starts_with(name, "symref"))
                        refname = ref->symref ? ref->symref : "";
-               else if (!prefixcmp(name, "upstream")) {
+               else if (starts_with(name, "upstream")) {
                        /* only local branches may have an upstream */
-                       if (prefixcmp(ref->refname, "refs/heads/"))
+                       if (!starts_with(ref->refname, "refs/heads/"))
                                continue;
                        branch = branch_get(ref->refname + 11);
 
@@ -682,7 +682,7 @@ static void populate_value(struct refinfo *ref)
                            !branch->merge[0]->dst)
                                continue;
                        refname = branch->merge[0]->dst;
-               } else if (!prefixcmp(name, "color:")) {
+               } else if (starts_with(name, "color:")) {
                        char color[COLOR_MAXLEN] = "";
 
                        color_parse(name + 6, "--format", color);
@@ -725,7 +725,7 @@ static void populate_value(struct refinfo *ref)
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
                        else if (!strcmp(formatp, "track") &&
-                               !prefixcmp(name, "upstream")) {
+                                starts_with(name, "upstream")) {
                                char buf[40];
 
                                stat_tracking_info(branch, &num_ours, &num_theirs);
@@ -744,7 +744,7 @@ static void populate_value(struct refinfo *ref)
                                }
                                continue;
                        } else if (!strcmp(formatp, "trackshort") &&
-                               !prefixcmp(name, "upstream")) {
+                                  starts_with(name, "upstream")) {
                                assert(branch);
                                stat_tracking_info(branch, &num_ours, &num_theirs);
                                if (!num_ours && !num_theirs)
@@ -1105,7 +1105,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        if (!sort)
                sort = default_sort();
-       sort_atom_limit = used_atom_cnt;
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
index 97ce678c6ba63afeb3deedfd469c4a53a475a1be..1affdd5e924c9e71605be603f253e34f6a995f36 100644 (file)
@@ -442,7 +442,7 @@ static void fsck_dir(int i, char *path)
                        add_sha1_list(sha1, DIRENT_SORT_HINT(de));
                        continue;
                }
-               if (!prefixcmp(de->d_name, "tmp_obj_"))
+               if (starts_with(de->d_name, "tmp_obj_"))
                        continue;
                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
        }
@@ -484,7 +484,7 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in
 
 static int is_branch(const char *refname)
 {
-       return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
 static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
@@ -566,7 +566,7 @@ static int fsck_head_link(void)
        if (!strcmp(head_points_at, "HEAD"))
                /* detached HEAD */
                null_is_error = 1;
-       else if (prefixcmp(head_points_at, "refs/heads/"))
+       else if (!starts_with(head_points_at, "refs/heads/"))
                return error("HEAD points to something strange (%s)",
                             head_points_at);
        if (is_null_sha1(head_sha1)) {
index f1e236b9124f8b7bfd9aa5e926d525c6f4568268..cc17e670ceb458491cc47e463b970b6d4c6143f8 100644 (file)
@@ -100,7 +100,7 @@ static int check_emacsclient_version(void)
         */
        finish_command(&ec_process);
 
-       if (prefixcmp(buffer.buf, "emacsclient")) {
+       if (!starts_with(buffer.buf, "emacsclient")) {
                strbuf_release(&buffer);
                return error(_("Failed to parse emacsclient version."));
        }
@@ -258,7 +258,7 @@ static int add_man_viewer_info(const char *var, const char *value)
 
 static int git_help_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "help", &colopts);
        if (!strcmp(var, "help.format")) {
                if (!value)
@@ -278,7 +278,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
                add_man_viewer(value);
                return 0;
        }
-       if (!prefixcmp(var, "man."))
+       if (starts_with(var, "man."))
                return add_man_viewer_info(var, value);
 
        return git_default_config(var, value, cb);
@@ -306,7 +306,7 @@ static const char *cmd_to_page(const char *git_cmd)
 {
        if (!git_cmd)
                return "git";
-       else if (!prefixcmp(git_cmd, "git"))
+       else if (starts_with(git_cmd, "git"))
                return git_cmd;
        else if (is_git_command(git_cmd))
                return prepend("git-", git_cmd);
index 9e9eb4b74e8335f097c7cc9e261b051feda0bf0d..2f37a38fbcdf131ab5897e47728c5773ba36b861 100644 (file)
@@ -1534,9 +1534,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                stat_only = 1;
                        } else if (!strcmp(arg, "--keep")) {
                                keep_msg = "";
-                       } else if (!prefixcmp(arg, "--keep=")) {
+                       } else if (starts_with(arg, "--keep=")) {
                                keep_msg = arg + 7;
-                       } else if (!prefixcmp(arg, "--threads=")) {
+                       } else if (starts_with(arg, "--threads=")) {
                                char *end;
                                nr_threads = strtoul(arg+10, &end, 0);
                                if (!arg[10] || *end || nr_threads < 0)
@@ -1547,7 +1547,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                                  "ignoring %s"), arg);
                                nr_threads = 1;
 #endif
-                       } else if (!prefixcmp(arg, "--pack_header=")) {
+                       } else if (starts_with(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
 
@@ -1566,7 +1566,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                if (index_name || (i+1) >= argc)
                                        usage(index_pack_usage);
                                index_name = argv[++i];
-                       } else if (!prefixcmp(arg, "--index-version=")) {
+                       } else if (starts_with(arg, "--index-version=")) {
                                char *c;
                                opts.version = strtoul(arg + 16, &c, 10);
                                if (opts.version > 2)
index 78aa3872dddbba8988b61c49996f9e63719bac75..b3f03cf0d6c0f776b2eaba8ff4e1d340216a4463 100644 (file)
@@ -266,7 +266,7 @@ static int create_default_files(const char *template_path)
                /* allow template config file to override the default */
                if (log_all_ref_updates == -1)
                    git_config_set("core.logallrefupdates", "true");
-               if (prefixcmp(git_dir, work_tree) ||
+               if (!starts_with(git_dir, work_tree) ||
                    strcmp(git_dir + strlen(work_tree), "/.git")) {
                        git_config_set("core.worktree", work_tree);
                }
index b708517a3506517a25d4c05f3c9c92a660688f8d..b97373da3b2687e18b5eba96382049382d1536fd 100644 (file)
@@ -391,7 +391,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
                default_show_root = git_config_bool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.decorate."))
+       if (starts_with(var, "color.decorate."))
                return parse_decorate_color_config(var, 15, value);
        if (!strcmp(var, "log.mailmap")) {
                use_mailmap_config = git_config_bool(var, value);
@@ -477,7 +477,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
                int new_offset = offset + 1;
                while (new_offset < size && buf[new_offset++] != '\n')
                        ; /* do nothing */
-               if (!prefixcmp(buf + offset, "tagger "))
+               if (starts_with(buf + offset, "tagger "))
                        show_tagger(buf + offset + 7,
                                    new_offset - offset - 7, rev);
                offset = new_offset;
@@ -882,7 +882,7 @@ static char *find_branch_name(struct rev_info *rev)
        ref = rev->cmdline.rev[positive].name;
        tip_sha1 = rev->cmdline.rev[positive].item->sha1;
        if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
-           !prefixcmp(full_ref, "refs/heads/") &&
+           starts_with(full_ref, "refs/heads/") &&
            !hashcmp(tip_sha1, branch_sha1))
                branch = xstrdup(full_ref + strlen("refs/heads/"));
        free(full_ref);
@@ -1388,7 +1388,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        unsigned char sha1[20];
                        const char *ref;
                        ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
-                       if (ref && !prefixcmp(ref, "refs/heads/"))
+                       if (ref && starts_with(ref, "refs/heads/"))
                                branch_name = xstrdup(ref + strlen("refs/heads/"));
                        else
                                branch_name = xstrdup(""); /* no branch */
index 25e83cfe9de3dc3cee19f03f82b56396eb6b65de..39e5144b9e38f1319f8fb881af6b1d34ad0c4089 100644 (file)
@@ -50,11 +50,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
 
                if (*arg == '-') {
-                       if (!prefixcmp(arg, "--upload-pack=")) {
+                       if (starts_with(arg, "--upload-pack=")) {
                                uploadpack = arg + 14;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exec=")) {
+                       if (starts_with(arg, "--exec=")) {
                                uploadpack = arg + 7;
                                continue;
                        }
index 24a772d8e1b7355a58088d784fdc18cd54302b7d..2c3cd8eab7073669001ad605020577279ba7a07d 100644 (file)
@@ -328,11 +328,11 @@ static int check_header(const struct strbuf *line,
        }
 
        /* for inbody stuff */
-       if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) {
+       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
                ret = 1; /* Should this return 0? */
                goto check_header_out;
        }
-       if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
                for (i = 0; header[i]; i++) {
                        if (!memcmp("Subject", header[i], 7)) {
                                handle_header(&hdr_data[i], line);
@@ -361,7 +361,7 @@ static int is_rfc2822_header(const struct strbuf *line)
        char *cp = line->buf;
 
        /* Count mbox From headers as headers */
-       if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From "))
+       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
                return 1;
 
        while ((ch = *cp++)) {
@@ -671,11 +671,11 @@ static inline int patchbreak(const struct strbuf *line)
        size_t i;
 
        /* Beginning of a "diff -" header? */
-       if (!prefixcmp(line->buf, "diff -"))
+       if (starts_with(line->buf, "diff -"))
                return 1;
 
        /* CVS "Index: " line? */
-       if (!prefixcmp(line->buf, "Index: "))
+       if (starts_with(line->buf, "Index: "))
                return 1;
 
        /*
@@ -685,7 +685,7 @@ static inline int patchbreak(const struct strbuf *line)
        if (line->len < 4)
                return 0;
 
-       if (!prefixcmp(line->buf, "---")) {
+       if (starts_with(line->buf, "---")) {
                /* space followed by a filename? */
                if (line->buf[3] == ' ' && !isspace(line->buf[4]))
                        return 1;
@@ -986,7 +986,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 
 static int git_mailinfo_config(const char *var, const char *value, void *unused)
 {
-       if (prefixcmp(var, "mailinfo."))
+       if (!starts_with(var, "mailinfo."))
                return git_default_config(var, value, unused);
        if (!strcmp(var, "mailinfo.scissors")) {
                use_scissors = git_config_bool(var, value);
@@ -1020,7 +1020,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
                        metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
                        metainfo_charset = NULL;
-               else if (!prefixcmp(argv[1], "--encoding="))
+               else if (starts_with(argv[1], "--encoding="))
                        metainfo_charset = argv[1] + 11;
                else if (!strcmp(argv[1], "--scissors"))
                        use_scissors = 1;
index 3a64f5d0bdbcc8d7d21edb01f285cc8b41755228..a90f28f34d2072b7d40964a7fa3cf46d3bb98cdd 100644 (file)
@@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        struct commit *result;
 
        init_merge_options(&o);
-       if (argv[0] && !suffixcmp(argv[0], "-subtree"))
+       if (argv[0] && ends_with(argv[0], "-subtree"))
                o.subtree_shift = "";
 
        if (argc < 4)
@@ -38,7 +38,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; ++i) {
                const char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--")) {
+               if (starts_with(arg, "--")) {
                        if (!arg[2])
                                break;
                        if (parse_merge_opt(&o, arg + 2))
index 41fb66dec2c2fd9645a72c28c362067fb3bcc16c..4941a6c36ab215fdb709ac58a3aea69218025dbb 100644 (file)
@@ -446,17 +446,17 @@ static void merge_name(const char *remote, struct strbuf *msg)
                die(_("'%s' does not point to a commit"), remote);
 
        if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
-               if (!prefixcmp(found_ref, "refs/heads/")) {
+               if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
-               if (!prefixcmp(found_ref, "refs/tags/")) {
+               if (starts_with(found_ref, "refs/tags/")) {
                        strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
-               if (!prefixcmp(found_ref, "refs/remotes/")) {
+               if (starts_with(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
@@ -570,8 +570,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 {
        int status;
 
-       if (branch && !prefixcmp(k, "branch.") &&
-               !prefixcmp(k + 7, branch) &&
+       if (branch && starts_with(k, "branch.") &&
+               starts_with(k + 7, branch) &&
                !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
                free(branch_mergeoptions);
                branch_mergeoptions = xstrdup(v);
@@ -1106,7 +1106,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * current branch.
         */
        branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
-       if (branch && !prefixcmp(branch, "refs/heads/"))
+       if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
        if (!branch || is_null_sha1(head_sha1))
                head_commit = NULL;
index 2e0e61b6517d74edc8b7ef2e56c75927385e7c85..08fbc033e45bba4880c8de797ef5ac0dc0b6259d 100644 (file)
@@ -16,9 +16,12 @@ static const char * const builtin_mv_usage[] = {
        NULL
 };
 
+#define DUP_BASENAME 1
+#define KEEP_TRAILING_SLASH 2
+
 static const char **internal_copy_pathspec(const char *prefix,
                                           const char **pathspec,
-                                          int count, int base_name)
+                                          int count, unsigned flags)
 {
        int i;
        const char **result = xmalloc((count + 1) * sizeof(const char *));
@@ -27,11 +30,12 @@ static const char **internal_copy_pathspec(const char *prefix,
        for (i = 0; i < count; i++) {
                int length = strlen(result[i]);
                int to_copy = length;
-               while (to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
+               while (!(flags & KEEP_TRAILING_SLASH) &&
+                      to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
                        to_copy--;
-               if (to_copy != length || base_name) {
+               if (to_copy != length || flags & DUP_BASENAME) {
                        char *it = xmemdupz(result[i], to_copy);
-                       if (base_name) {
+                       if (flags & DUP_BASENAME) {
                                result[i] = xstrdup(basename(it));
                                free(it);
                        } else
@@ -87,16 +91,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
        source = internal_copy_pathspec(prefix, argv, argc, 0);
        modes = xcalloc(argc, sizeof(enum update_mode));
-       dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
+       /*
+        * Keep trailing slash, needed to let
+        * "git mv file no-such-dir/" error out.
+        */
+       dest_path = internal_copy_pathspec(prefix, argv + argc, 1,
+                                          KEEP_TRAILING_SLASH);
        submodule_gitfile = xcalloc(argc, sizeof(char *));
 
        if (dest_path[0][0] == '\0')
                /* special case: "." was normalized to "" */
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        else if (!lstat(dest_path[0], &st) &&
                        S_ISDIR(st.st_mode)) {
                dest_path[0] = add_slash(dest_path[0]);
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        } else {
                if (argc != 1)
                        die("destination '%s' is not a directory", dest_path[0]);
index 23daaa7d99b40e1bbd38a2743bc0d9ed0c38eda9..0b21d7e5b297ff3d2d1a73d8a74c8bc37e672ccb 100644 (file)
@@ -100,9 +100,9 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
 {
        if (shorten_unambiguous)
                refname = shorten_unambiguous_ref(refname, 0);
-       else if (!prefixcmp(refname, "refs/heads/"))
+       else if (starts_with(refname, "refs/heads/"))
                refname = refname + 11;
-       else if (!prefixcmp(refname, "refs/"))
+       else if (starts_with(refname, "refs/"))
                refname = refname + 5;
        return refname;
 }
@@ -148,7 +148,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
        int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
 
-       if (data->tags_only && prefixcmp(path, "refs/tags/"))
+       if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
 
        if (data->ref_filter) {
index d459e23c4225ae0f8084cfd756563c2b7ab85a60..2b24d059b509d28d587066440ed131ce2f85464f 100644 (file)
@@ -347,7 +347,7 @@ static struct notes_tree *init_notes_check(const char *subcommand)
        init_notes(NULL, NULL, NULL, 0);
        t = &default_notes_tree;
 
-       if (prefixcmp(t->ref, "refs/notes/"))
+       if (!starts_with(t->ref, "refs/notes/"))
                die("Refusing to %s notes in %s (outside of refs/notes/)",
                    subcommand, t->ref);
        return t;
index 36273dd6f0ebf3eab1062a0e24b9aa0c1834ce02..dfb4d84caa506cee874fd972bc0e9df1a4121362 100644 (file)
@@ -2032,7 +2032,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
 {
        unsigned char peeled[20];
 
-       if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+       if (starts_with(path, "refs/tags/") && /* is a tag? */
            !peel_ref(path, peeled)        && /* peelable? */
            locate_object_entry(peeled))      /* object packed? */
                add_object_entry(sha1, OBJ_TAG, NULL, 0);
index 6366917c6de55e171a7f697b22f1de10293addf5..a85ef54d5aeb679067c7f3ad428439886a162362 100644 (file)
@@ -80,7 +80,7 @@ static int prune_dir(int i, char *path)
                        prune_object(path, de->d_name, sha1);
                        continue;
                }
-               if (!prefixcmp(de->d_name, "tmp_obj_")) {
+               if (starts_with(de->d_name, "tmp_obj_")) {
                        prune_tmp_object(path, de->d_name);
                        continue;
                }
@@ -119,7 +119,7 @@ static void remove_temporary_files(const char *path)
                return;
        }
        while ((de = readdir(dir)) != NULL)
-               if (!prefixcmp(de->d_name, "tmp_"))
+               if (starts_with(de->d_name, "tmp_"))
                        prune_tmp_object(path, de->d_name);
        closedir(dir);
 }
index a73982a30879f89be939034e462df68ee2c51c0b..0e50ddbb01342128d9118217118726000bdeaff6 100644 (file)
@@ -35,35 +35,75 @@ static void add_refspec(const char *ref)
        refspec[refspec_nr-1] = ref;
 }
 
-static void set_refspecs(const char **refs, int nr)
+static const char *map_refspec(const char *ref,
+                              struct remote *remote, struct ref *local_refs)
 {
+       struct ref *matched = NULL;
+
+       /* Does "ref" uniquely name our ref? */
+       if (count_refspec_match(ref, local_refs, &matched) != 1)
+               return ref;
+
+       if (remote->push) {
+               struct refspec query;
+               memset(&query, 0, sizeof(struct refspec));
+               query.src = matched->name;
+               if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
+                   query.dst) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s%s:%s",
+                                   query.force ? "+" : "",
+                                   query.src, query.dst);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       if (push_default == PUSH_DEFAULT_UPSTREAM &&
+           !prefixcmp(matched->name, "refs/heads/")) {
+               struct branch *branch = branch_get(matched->name + 11);
+               if (branch->merge_nr == 1 && branch->merge[0]->src) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s:%s",
+                                   ref, branch->merge[0]->src);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       return ref;
+}
+
+static void set_refspecs(const char **refs, int nr, const char *repo)
+{
+       struct remote *remote = NULL;
+       struct ref *local_refs = NULL;
        int i;
+
        for (i = 0; i < nr; i++) {
                const char *ref = refs[i];
                if (!strcmp("tag", ref)) {
-                       char *tag;
-                       int len;
+                       struct strbuf tagref = STRBUF_INIT;
                        if (nr <= ++i)
                                die(_("tag shorthand without <tag>"));
-                       len = strlen(refs[i]) + 11;
-                       if (deleterefs) {
-                               tag = xmalloc(len+1);
-                               strcpy(tag, ":refs/tags/");
-                       } else {
-                               tag = xmalloc(len);
-                               strcpy(tag, "refs/tags/");
+                       ref = refs[i];
+                       if (deleterefs)
+                               strbuf_addf(&tagref, ":refs/tags/%s", ref);
+                       else
+                               strbuf_addf(&tagref, "refs/tags/%s", ref);
+                       ref = strbuf_detach(&tagref, NULL);
+               } else if (deleterefs) {
+                       struct strbuf delref = STRBUF_INIT;
+                       if (strchr(ref, ':'))
+                               die(_("--delete only accepts plain target ref names"));
+                       strbuf_addf(&delref, ":%s", ref);
+                       ref = strbuf_detach(&delref, NULL);
+               } else if (!strchr(ref, ':')) {
+                       if (!remote) {
+                               /* lazily grab remote and local_refs */
+                               remote = remote_get(repo);
+                               local_refs = get_local_heads();
                        }
-                       strcat(tag, refs[i]);
-                       ref = tag;
-               } else if (deleterefs && !strchr(ref, ':')) {
-                       char *delref;
-                       int len = strlen(ref)+1;
-                       delref = xmalloc(len+1);
-                       strcpy(delref, ":");
-                       strcat(delref, ref);
-                       ref = delref;
-               } else if (deleterefs)
-                       die(_("--delete only accepts plain target ref names"));
+                       ref = map_refspec(ref, remote, local_refs);
+               }
                add_refspec(ref);
        }
 }
@@ -501,7 +541,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 
        if (argc > 0) {
                repo = argv[0];
-               set_refspecs(argv + 1, argc - 1);
+               set_refspecs(argv + 1, argc - 1, repo);
        }
 
        rc = do_push(repo, flags);
index 67ce1ef105d1494e7905e66750b14676e082e11c..e09994f4dfa3a50ea285ad20d61dd845f4bea357 100644 (file)
@@ -428,7 +428,7 @@ static const char *update(struct command *cmd)
        struct ref_lock *lock;
 
        /* only refs/... are allowed */
-       if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
+       if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
                rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
@@ -459,7 +459,7 @@ static const char *update(struct command *cmd)
        }
 
        if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
-               if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+               if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
@@ -483,7 +483,7 @@ static const char *update(struct command *cmd)
 
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
-           !prefixcmp(name, "refs/heads/")) {
+           starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
 
index 6eb24c8da27d265cad38628aeaa3e040dd298aab..852cff60b784d316cddb59234b6276dfe18fd75d 100644 (file)
@@ -610,12 +610,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        cb.dry_run = 1;
-               else if (!prefixcmp(arg, "--expire=")) {
+               else if (starts_with(arg, "--expire=")) {
                        if (parse_expiry_date(arg + 9, &cb.expire_total))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_TOTAL;
                }
-               else if (!prefixcmp(arg, "--expire-unreachable=")) {
+               else if (starts_with(arg, "--expire-unreachable=")) {
                        if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_UNREACH;
index f532f354579dc8af7905295a1f3c499e4c18fe11..b3ab4cf8f6517c715845db7e17c67cad5f50d730 100644 (file)
@@ -78,14 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = {
 
 static int verbose;
 
-static inline int postfixcmp(const char *string, const char *postfix)
-{
-       int len1 = strlen(string), len2 = strlen(postfix);
-       if (len1 < len2)
-               return 1;
-       return strcmp(string + len1 - len2, postfix);
-}
-
 static int fetch_remote(const char *name)
 {
        const char *argv[] = { "fetch", name, NULL, NULL };
@@ -267,7 +259,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                const char *orig_key = key;
                char *name;
                struct string_list_item *item;
@@ -275,13 +267,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                enum { REMOTE, MERGE, REBASE } type;
 
                key += 7;
-               if (!postfixcmp(key, ".remote")) {
+               if (ends_with(key, ".remote")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REMOTE;
-               } else if (!postfixcmp(key, ".merge")) {
+               } else if (ends_with(key, ".merge")) {
                        name = xstrndup(key, strlen(key) - 6);
                        type = MERGE;
-               } else if (!postfixcmp(key, ".rebase")) {
+               } else if (ends_with(key, ".rebase")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REBASE;
                } else
@@ -307,8 +299,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                                space = strchr(value, ' ');
                        }
                        string_list_append(&info->merge, xstrdup(value));
-               } else
-                       info->rebase = git_config_bool(orig_key, value);
+               } else {
+                       int v = git_config_maybe_bool(orig_key, value);
+                       if (v >= 0)
+                               info->rebase = v;
+                       else if (!strcmp(value, "preserve"))
+                               info->rebase = 1;
+               }
        }
        return 0;
 }
@@ -532,9 +529,9 @@ static int add_branch_for_removal(const char *refname,
        }
 
        /* don't delete non-remote-tracking refs */
-       if (prefixcmp(refname, "refs/remotes/")) {
+       if (!starts_with(refname, "refs/remotes/")) {
                /* advise user how to delete local branches */
-               if (!prefixcmp(refname, "refs/heads/"))
+               if (starts_with(refname, "refs/heads/"))
                        string_list_append(branches->skipped,
                                           abbrev_branch(refname));
                /* silently skip over other non-remote refs */
@@ -569,7 +566,7 @@ static int read_remote_branches(const char *refname,
        const char *symref;
 
        strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
-       if (!prefixcmp(refname, buf.buf)) {
+       if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
index a0ff5c704f4e21c5021091aa7335e00959993470..ba66c6e37782fcf1bad53b7986e69ec113c4521c 100644 (file)
@@ -78,7 +78,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
                return;
 
        while ((e = readdir(dir)) != NULL) {
-               if (suffixcmp(e->d_name, ".pack"))
+               if (!ends_with(e->d_name, ".pack"))
                        continue;
 
                len = strlen(e->d_name) - strlen(".pack");
@@ -123,7 +123,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct string_list rollback = STRING_LIST_INIT_NODUP;
        struct string_list existing_packs = STRING_LIST_INIT_DUP;
        struct strbuf line = STRBUF_INIT;
-       int nr_packs, ext, ret, failed;
+       int ext, ret, failed;
        FILE *out;
 
        /* variables to be filled by option parsing */
@@ -233,13 +233,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (ret)
                return ret;
 
-       nr_packs = 0;
        out = xfdopen(cmd.out, "r");
        while (strbuf_getline(&line, out, '\n') != EOF) {
                if (line.len != 40)
                        die("repack: Expecting 40 character sha1 lines only from pack-objects.");
                string_list_append(&names, line.buf);
-               nr_packs++;
        }
        fclose(out);
        ret = finish_command(&cmd);
@@ -247,7 +245,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                return ret;
        argv_array_clear(&cmd_args);
 
-       if (!nr_packs && !quiet)
+       if (!names.nr && !quiet)
                printf("Nothing new to pack.\n");
 
        /*
index b1bd3ef9946761b146a38d5977a7378175cda047..2336325ce330f5268800a7bc949c066ce028852c 100644 (file)
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
        N_("git replace -d <object>..."),
-       N_("git replace -l [<pattern>]"),
+       N_("git replace [--format=<format>] [-l [<pattern>]]"),
        NULL
 };
 
+enum replace_format {
+      REPLACE_FORMAT_SHORT,
+      REPLACE_FORMAT_MEDIUM,
+      REPLACE_FORMAT_LONG
+};
+
+struct show_data {
+       const char *pattern;
+       enum replace_format format;
+};
+
 static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
 {
-       const char *pattern = cb_data;
+       struct show_data *data = cb_data;
 
-       if (!fnmatch(pattern, refname, 0))
-               printf("%s\n", refname);
+       if (!fnmatch(data->pattern, refname, 0)) {
+               if (data->format == REPLACE_FORMAT_SHORT)
+                       printf("%s\n", refname);
+               else if (data->format == REPLACE_FORMAT_MEDIUM)
+                       printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+               else { /* data->format == REPLACE_FORMAT_LONG */
+                       unsigned char object[20];
+                       enum object_type obj_type, repl_type;
+
+                       if (get_sha1(refname, object))
+                               return error("Failed to resolve '%s' as a valid ref.", refname);
+
+                       obj_type = sha1_object_info(object, NULL);
+                       repl_type = sha1_object_info(sha1, NULL);
+
+                       printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
+                              sha1_to_hex(sha1), typename(repl_type));
+               }
+       }
 
        return 0;
 }
 
-static int list_replace_refs(const char *pattern)
+static int list_replace_refs(const char *pattern, const char *format)
 {
+       struct show_data data;
+
        if (pattern == NULL)
                pattern = "*";
+       data.pattern = pattern;
 
-       for_each_replace_ref(show_reference, (void *) pattern);
+       if (format == NULL || *format == '\0' || !strcmp(format, "short"))
+               data.format = REPLACE_FORMAT_SHORT;
+       else if (!strcmp(format, "medium"))
+               data.format = REPLACE_FORMAT_MEDIUM;
+       else if (!strcmp(format, "long"))
+               data.format = REPLACE_FORMAT_LONG;
+       else
+               die("invalid replace format '%s'\n"
+                   "valid formats are 'short', 'medium' and 'long'\n",
+                   format);
+
+       for_each_replace_ref(show_reference, (void *) &data);
 
        return 0;
 }
@@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref,
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int list = 0, delete = 0, force = 0;
+       const char *format = NULL;
        struct option options[] = {
                OPT_BOOL('l', "list", &list, N_("list replace refs")),
                OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
        };
 
+       read_replace_refs = 0;
+
        argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
 
        if (list && delete)
                usage_msg_opt("-l and -d cannot be used together",
                              git_replace_usage, options);
 
+       if (format && delete)
+               usage_msg_opt("--format and -d cannot be used together",
+                             git_replace_usage, options);
+
        if (force && (list || delete))
                usage_msg_opt("-f cannot be used with -d or -l",
                              git_replace_usage, options);
@@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                if (argc != 2)
                        usage_msg_opt("bad number of arguments",
                                      git_replace_usage, options);
+               if (format)
+                       usage_msg_opt("--format cannot be used when not listing",
+                                     git_replace_usage, options);
                return replace_object(argv[0], argv[1], force);
        }
 
@@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                usage_msg_opt("-f needs some arguments",
                              git_replace_usage, options);
 
-       return list_replace_refs(argv[0]);
+       return list_replace_refs(argv[0], format);
 }
index 1d9ecafd417cf763d0c33438b74cec28c463ed74..aaeb611a9729938251b1c679e35c446bb737ff4f 100644 (file)
@@ -286,6 +286,7 @@ static int try_difference(const char *arg)
                                exclude = n;
                        }
                }
+               *dotdot = '.';
                return 1;
        }
        *dotdot = '.';
@@ -309,8 +310,10 @@ static int try_parent_shorthands(const char *arg)
                return 0;
 
        *dotdot = 0;
-       if (get_sha1_committish(arg, sha1))
+       if (get_sha1_committish(arg, sha1)) {
+               *dotdot = '^';
                return 0;
+       }
 
        if (!parents_only)
                show_rev(NORMAL, sha1, arg);
@@ -319,6 +322,7 @@ static int try_parent_shorthands(const char *arg)
                show_rev(parents_only ? NORMAL : REVERSED,
                                parents->item->object.sha1, arg);
 
+       *dotdot = '^';
        return 1;
 }
 
@@ -488,6 +492,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int has_dashdash = 0;
        int output_prefix = 0;
        unsigned char sha1[20];
        const char *name = NULL;
@@ -501,6 +506,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
 
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       has_dashdash = 1;
+                       break;
+               }
+       }
+
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
@@ -520,7 +532,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        }
                        continue;
                }
-               if (!prefixcmp(arg, "-n")) {
+               if (starts_with(arg, "-n")) {
                        if ((filter & DO_FLAGS) && (filter & DO_REVS))
                                show(arg);
                        continue;
@@ -572,7 +584,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--short") ||
-                           !prefixcmp(arg, "--short=")) {
+                           starts_with(arg, "--short=")) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
@@ -600,7 +612,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--abbrev-ref") &&
+                       if (starts_with(arg, "--abbrev-ref") &&
                            (!arg[12] || arg[12] == '=')) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
@@ -618,7 +630,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--disambiguate=")) {
+                       if (starts_with(arg, "--disambiguate=")) {
                                for_each_abbrev(arg + 15, show_abbrev, NULL);
                                continue;
                        }
@@ -627,7 +639,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--branches=")) {
+                       if (starts_with(arg, "--branches=")) {
                                for_each_glob_ref_in(show_reference, arg + 11,
                                        "refs/heads/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -638,7 +650,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--tags=")) {
+                       if (starts_with(arg, "--tags=")) {
                                for_each_glob_ref_in(show_reference, arg + 7,
                                        "refs/tags/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -649,12 +661,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if (starts_with(arg, "--glob=")) {
                                for_each_glob_ref(show_reference, arg + 7, NULL);
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--remotes=")) {
+                       if (starts_with(arg, "--remotes=")) {
                                for_each_glob_ref_in(show_reference, arg + 10,
                                        "refs/remotes/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -665,7 +677,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exclude=")) {
+                       if (starts_with(arg, "--exclude=")) {
                                add_ref_exclusion(&ref_excludes, arg + 10);
                                continue;
                        }
@@ -747,19 +759,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
-                       if (!prefixcmp(arg, "--since=")) {
+                       if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--after=")) {
+                       if (starts_with(arg, "--after=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--before=")) {
+                       if (starts_with(arg, "--before=")) {
                                show_datestring("--min-age=", arg+9);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--until=")) {
+                       if (starts_with(arg, "--until=")) {
                                show_datestring("--min-age=", arg+8);
                                continue;
                        }
@@ -788,6 +800,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
                if (verify)
                        die_no_single_rev(quiet);
+               if (has_dashdash)
+                       die("bad revision '%s'", arg);
                as_is = 1;
                if (!show_file(arg, output_prefix))
                        continue;
index 4482f16efb66c7fc06c7446a2e992840cc83f798..e7f0b97d315e0866a2bb3a95a0d1feebceddd545 100644 (file)
@@ -115,15 +115,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                const char *arg = *argv;
 
                if (*arg == '-') {
-                       if (!prefixcmp(arg, "--receive-pack=")) {
+                       if (starts_with(arg, "--receive-pack=")) {
                                receivepack = arg + 15;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exec=")) {
+                       if (starts_with(arg, "--exec=")) {
                                receivepack = arg + 7;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--remote=")) {
+                       if (starts_with(arg, "--remote=")) {
                                remote_name = arg + 9;
                                continue;
                        }
@@ -181,7 +181,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                        exit(1);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) {
+                       if (starts_with(arg, "--" CAS_OPT_NAME "=")) {
                                if (parse_push_cas_option(&cas,
                                                          strchr(arg, '=') + 1, 0) < 0)
                                        exit(1);
index c226f767aa9b0e5633a3d5dde7b0b969fec39c7f..4b7e53623fc9cf0c86ed0e7e87d4b3357d765d86 100644 (file)
@@ -65,7 +65,7 @@ static void insert_one_record(struct shortlog *log,
        eol = strchr(oneline, '\n');
        if (!eol)
                eol = oneline + strlen(oneline);
-       if (!prefixcmp(oneline, "[PATCH")) {
+       if (starts_with(oneline, "[PATCH")) {
                char *eob = strchr(oneline, ']');
                if (eob && (!eol || eob < eol))
                        oneline = eob + 1;
@@ -95,7 +95,7 @@ static void read_from_stdin(struct shortlog *log)
 
        while (fgets(author, sizeof(author), stdin) != NULL) {
                if (!(author[0] == 'A' || author[0] == 'a') ||
-                   prefixcmp(author + 1, "uthor: "))
+                   !starts_with(author + 1, "uthor: "))
                        continue;
                while (fgets(oneline, sizeof(oneline), stdin) &&
                       oneline[0] != '\n')
@@ -123,7 +123,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                else
                        eol++;
 
-               if (!prefixcmp(buffer, "author "))
+               if (starts_with(buffer, "author "))
                        author = buffer + 7;
                buffer = eol;
        }
index 46902c3de40b05b19ff1b5b0127b9f1cd11f6cde..d9217ce1e1a957462284224c08786db77f5a8f15 100644 (file)
@@ -284,7 +284,7 @@ static void show_one_commit(struct commit *commit, int no_name)
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
                pretty_str = pretty.buf;
        }
-       if (!prefixcmp(pretty_str, "[PATCH] "))
+       if (starts_with(pretty_str, "[PATCH] "))
                pretty_str += 8;
 
        if (!no_name) {
@@ -395,7 +395,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f
 {
        unsigned char tmp[20];
        int ofs = 11;
-       if (prefixcmp(refname, "refs/heads/"))
+       if (!starts_with(refname, "refs/heads/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
@@ -409,7 +409,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
 {
        unsigned char tmp[20];
        int ofs = 13;
-       if (prefixcmp(refname, "refs/remotes/"))
+       if (!starts_with(refname, "refs/remotes/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
@@ -421,7 +421,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
 
 static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (prefixcmp(refname, "refs/tags/"))
+       if (!starts_with(refname, "refs/tags/"))
                return 0;
        return append_ref(refname + 5, sha1, 0);
 }
@@ -452,9 +452,9 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i
                return 0;
        if (fnmatch(match_ref_pattern, tail, 0))
                return 0;
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                return append_head_ref(refname, sha1, flag, cb_data);
-       if (!prefixcmp(refname, "refs/tags/"))
+       if (starts_with(refname, "refs/tags/"))
                return append_tag_ref(refname, sha1, flag, cb_data);
        return append_ref(refname, sha1, 0);
 }
@@ -479,11 +479,11 @@ static int rev_is_head(char *head, int headlen, char *name,
        if ((!head[0]) ||
            (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
                return 0;
-       if (!prefixcmp(head, "refs/heads/"))
+       if (starts_with(head, "refs/heads/"))
                head += 11;
-       if (!prefixcmp(name, "refs/heads/"))
+       if (starts_with(name, "refs/heads/"))
                name += 11;
-       else if (!prefixcmp(name, "heads/"))
+       else if (starts_with(name, "heads/"))
                name += 6;
        return !strcmp(head, name);
 }
@@ -812,7 +812,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                has_head++;
                }
                if (!has_head) {
-                       int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+                       int offset = starts_with(head, "refs/heads/") ? 11 : 0;
                        append_one_rev(head + offset);
                }
        }
index 9f3f5e370bfb123df5c662bead122b29622fc1dd..5ba1f3083890c693f8ce82166fe1cb1f6440b106 100644 (file)
@@ -37,8 +37,8 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
        if (tags_only || heads_only) {
                int match;
 
-               match = heads_only && !prefixcmp(refname, "refs/heads/");
-               match |= tags_only && !prefixcmp(refname, "refs/tags/");
+               match = heads_only && starts_with(refname, "refs/heads/");
+               match |= tags_only && starts_with(refname, "refs/tags/");
                if (!match)
                        return 0;
        }
@@ -210,7 +210,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
                while (*pattern) {
                        unsigned char sha1[20];
 
-                       if (!prefixcmp(*pattern, "refs/") &&
+                       if (starts_with(*pattern, "refs/") &&
                            !read_ref(*pattern, sha1)) {
                                if (!quiet)
                                        show_one(*pattern, sha1);
index 71286b4fae4792dce73ab1533204e803e2a61334..b6a711d3191a729e5f07aae20e3fc5343148c9b2 100644 (file)
@@ -65,7 +65,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                break;
        case 2:
                if (!strcmp(argv[0], "HEAD") &&
-                   prefixcmp(argv[1], "refs/"))
+                   !starts_with(argv[1], "refs/"))
                        die("Refusing to point HEAD outside of refs/");
                create_symref(argv[0], argv[1], msg);
                break;
index ea55f1d1bdd524abd46e6baa7a02ef85d56d5eb2..74d3780b77548fc8a7bb2f51b47a51cbfb51969a 100644 (file)
@@ -259,7 +259,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        int status = git_gpg_config(var, value, cb);
        if (status)
                return status;
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "tag", &colopts);
        return git_default_config(var, value, cb);
 }
index 2217d7b3ae57235fc25ac3f5bf8429588aa034c4..62ff673f68b48cc2147fc7f7b17dc5269da022bf 100644 (file)
@@ -523,7 +523,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
                                strict = 1;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--pack_header=")) {
+                       if (starts_with(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
 
index 702e90db2a82a0eba2ff233b9ec46690da5afc3e..1292cfea11d87e8c98ba64ffae9d6a1aeffaca9f 100644 (file)
@@ -229,15 +229,15 @@ static void update_refs_stdin(void)
                        die("empty command in input");
                else if (isspace(*cmd.buf))
                        die("whitespace before command: %s", cmd.buf);
-               else if (!prefixcmp(cmd.buf, "update "))
+               else if (starts_with(cmd.buf, "update "))
                        parse_cmd_update(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "create "))
+               else if (starts_with(cmd.buf, "create "))
                        parse_cmd_create(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "delete "))
+               else if (starts_with(cmd.buf, "delete "))
                        parse_cmd_delete(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "verify "))
+               else if (starts_with(cmd.buf, "verify "))
                        parse_cmd_verify(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "option "))
+               else if (starts_with(cmd.buf, "option "))
                        parse_cmd_option(cmd.buf + 7);
                else
                        die("unknown command: %s", cmd.buf);
index af2da35e7d05f4de2d7a77a708ee3cf6f490266e..32ab94cd06b43cd05316f9a16981adc883c0d066 100644 (file)
@@ -37,7 +37,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
                if (sent_argv.argc > MAX_ARGS)
                        die("Too many options (>%d)", MAX_ARGS - 1);
 
-               if (prefixcmp(buf, arg_cmd))
+               if (!starts_with(buf, arg_cmd))
                        die("'argument' token or flush expected");
                argv_array_push(&sent_argv, buf + strlen(arg_cmd));
        }
diff --git a/cache.h b/cache.h
index ce377e1354a4d0fd719b50edb32124110956467f..9ba9773edfdeb0ffc543bdb3eb02ea16eee427ef 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -760,11 +760,11 @@ int daemon_avoid_alias(const char *path);
 int offset_1st_component(const char *path);
 
 /* object replacement */
-#define READ_SHA1_FILE_REPLACE 1
+#define LOOKUP_REPLACE_OBJECT 1
 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
 static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 {
-       return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
+       return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
 }
 extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
@@ -773,6 +773,12 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
                return sha1;
        return do_lookup_replace_object(sha1);
 }
+static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
+{
+       if (!(flag & LOOKUP_REPLACE_OBJECT))
+               return sha1;
+       return lookup_replace_object(sha1);
+}
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -1098,7 +1104,7 @@ struct object_info {
                } packed;
        } u;
 };
-extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
+extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 
 /* Dumb servers support */
 extern int update_server_info(int);
index 11509ffc471c548a1367068b3d7c2f15d19361f4..5df1df73512ebb90f558ce6f2f7e899e75f03ff8 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -566,7 +566,7 @@ static void record_author_date(struct author_date_slab *author_date,
             buf;
             buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
-               if (prefixcmp(buf, "author ")) {
+               if (!starts_with(buf, "author ")) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
@@ -1113,7 +1113,7 @@ int parse_signed_commit(const unsigned char *sha1,
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                        sig = line + 1;
-               else if (!prefixcmp(line, gpg_sig_header) &&
+               else if (starts_with(line, gpg_sig_header) &&
                         line[gpg_sig_header_len] == ' ')
                        sig = line + gpg_sig_header_len + 1;
                if (sig) {
@@ -1193,7 +1193,7 @@ static void parse_gpg_output(struct signature_check *sigc)
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
 
-               if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) {
+               if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) {
                        /* At the very beginning of the buffer */
                        found = buf + strlen(sigcheck_gpg_status[i].check + 1);
                } else {
index a2c22ab43c912ad1a509c07bd929f34221a0bf99..d969a5aefc2bca92938d3fd7f6a507e884ce2b7f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -969,25 +969,25 @@ static int git_default_mailmap_config(const char *var, const char *value)
 
 int git_default_config(const char *var, const char *value, void *dummy)
 {
-       if (!prefixcmp(var, "core."))
+       if (starts_with(var, "core."))
                return git_default_core_config(var, value);
 
-       if (!prefixcmp(var, "user."))
+       if (starts_with(var, "user."))
                return git_ident_config(var, value, dummy);
 
-       if (!prefixcmp(var, "i18n."))
+       if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
 
-       if (!prefixcmp(var, "branch."))
+       if (starts_with(var, "branch."))
                return git_default_branch_config(var, value);
 
-       if (!prefixcmp(var, "push."))
+       if (starts_with(var, "push."))
                return git_default_push_config(var, value);
 
-       if (!prefixcmp(var, "mailmap."))
+       if (starts_with(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
-       if (!prefixcmp(var, "advice."))
+       if (starts_with(var, "advice."))
                return git_default_advice_config(var, value);
 
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
@@ -1879,7 +1879,7 @@ int parse_config_key(const char *var,
        const char *dot;
 
        /* Does it start with "section." ? */
-       if (prefixcmp(var, section) || var[section_len] != '.')
+       if (!starts_with(var, section) || var[section_len] != '.')
                return -1;
 
        /*
index 06e88b0705f7fbd138e504920c20c70218121696..c763eeda863d62093a9f37d281e8b1afc1a1e254 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -145,7 +145,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                if (!len)
                        break;
 
-               if (len > 4 && !prefixcmp(buffer, "ERR "))
+               if (len > 4 && starts_with(buffer, "ERR "))
                        die("remote error: %s", buffer + 4);
 
                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
@@ -232,10 +232,34 @@ int server_supports(const char *feature)
 
 enum protocol {
        PROTO_LOCAL = 1,
+       PROTO_FILE,
        PROTO_SSH,
        PROTO_GIT
 };
 
+int url_is_local_not_ssh(const char *url)
+{
+       const char *colon = strchr(url, ':');
+       const char *slash = strchr(url, '/');
+       return !colon || (slash && slash < colon) ||
+               has_dos_drive_prefix(url);
+}
+
+static const char *prot_name(enum protocol protocol)
+{
+       switch (protocol) {
+               case PROTO_LOCAL:
+               case PROTO_FILE:
+                       return "file";
+               case PROTO_SSH:
+                       return "ssh";
+               case PROTO_GIT:
+                       return "git";
+               default:
+                       return "unkown protocol";
+       }
+}
+
 static enum protocol get_protocol(const char *name)
 {
        if (!strcmp(name, "ssh"))
@@ -247,7 +271,7 @@ static enum protocol get_protocol(const char *name)
        if (!strcmp(name, "ssh+git"))
                return PROTO_SSH;
        if (!strcmp(name, "file"))
-               return PROTO_LOCAL;
+               return PROTO_FILE;
        die("I don't handle protocol '%s'", name);
 }
 
@@ -527,55 +551,31 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
        return proxy;
 }
 
-#define MAX_CMD_LEN 1024
-
-static char *get_port(char *host)
+static const char *get_port_numeric(const char *p)
 {
        char *end;
-       char *p = strchr(host, ':');
-
        if (p) {
                long port = strtol(p + 1, &end, 10);
                if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
-                       *p = '\0';
-                       return p+1;
+                       return p;
                }
        }
 
        return NULL;
 }
 
-static struct child_process no_fork;
-
 /*
- * This returns a dummy child_process if the transport protocol does not
- * need fork(2), or a struct child_process object if it does.  Once done,
- * finish the connection with finish_connect() with the value returned from
- * this function (it is safe to call finish_connect() with NULL to support
- * the former case).
- *
- * If it returns, the connect is successful; it just dies on errors (this
- * will hopefully be changed in a libification effort, to return NULL when
- * the connection failed).
+ * Extract protocol and relevant parts from the specified connection URL.
+ * The caller must free() the returned strings.
  */
-struct child_process *git_connect(int fd[2], const char *url_orig,
-                                 const char *prog, int flags)
+static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
+                                      char **ret_path)
 {
        char *url;
        char *host, *path;
        char *end;
-       int c;
-       struct child_process *conn = &no_fork;
+       int separator = '/';
        enum protocol protocol = PROTO_LOCAL;
-       int free_path = 0;
-       char *port = NULL;
-       const char **arg;
-       struct strbuf cmd;
-
-       /* Without this we cannot rely on waitpid() to tell
-        * what happened to our children.
-        */
-       signal(SIGCHLD, SIG_DFL);
 
        if (is_url(url_orig))
                url = url_decode(url_orig);
@@ -587,40 +587,33 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                *host = '\0';
                protocol = get_protocol(url);
                host += 3;
-               c = '/';
        } else {
                host = url;
-               c = ':';
+               if (!url_is_local_not_ssh(url)) {
+                       protocol = PROTO_SSH;
+                       separator = ':';
+               }
        }
 
        /*
-        * Don't do destructive transforms with git:// as that
-        * protocol code does '[]' unwrapping of its own.
+        * Don't do destructive transforms as protocol code does
+        * '[]' unwrapping in get_host_and_port()
         */
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
                if (end) {
-                       if (protocol != PROTO_GIT) {
-                               *end = 0;
-                               host++;
-                       }
                        end++;
                } else
                        end = host;
        } else
                end = host;
 
-       path = strchr(end, c);
-       if (path && !has_dos_drive_prefix(end)) {
-               if (c == ':') {
-                       if (host != url || path < strchrnul(host, '/')) {
-                               protocol = PROTO_SSH;
-                               *path++ = '\0';
-                       } else /* '/' in the host part, assume local path */
-                               path = end;
-               }
-       } else
+       if (protocol == PROTO_LOCAL)
                path = end;
+       else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
+               path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
+       else
+               path = strchr(end, separator);
 
        if (!path || !*path)
                die("No path specified. See 'man git-pull' for valid url syntax");
@@ -629,33 +622,67 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         * null-terminate hostname and point path to ~ for URL's like this:
         *    ssh://host.xz/~user/repo
         */
-       if (protocol != PROTO_LOCAL && host != url) {
-               char *ptr = path;
+
+       end = path; /* Need to \0 terminate host here */
+       if (separator == ':')
+               path++; /* path starts after ':' */
+       if (protocol == PROTO_GIT || protocol == PROTO_SSH) {
                if (path[1] == '~')
                        path++;
-               else {
-                       path = xstrdup(ptr);
-                       free_path = 1;
-               }
-
-               *ptr = '\0';
        }
 
-       /*
-        * Add support for ssh port: ssh://host.xy:<port>/...
+       path = xstrdup(path);
+       *end = '\0';
+
+       *ret_host = xstrdup(host);
+       *ret_path = path;
+       free(url);
+       return protocol;
+}
+
+static struct child_process no_fork;
+
+/*
+ * This returns a dummy child_process if the transport protocol does not
+ * need fork(2), or a struct child_process object if it does.  Once done,
+ * finish the connection with finish_connect() with the value returned from
+ * this function (it is safe to call finish_connect() with NULL to support
+ * the former case).
+ *
+ * If it returns, the connect is successful; it just dies on errors (this
+ * will hopefully be changed in a libification effort, to return NULL when
+ * the connection failed).
+ */
+struct child_process *git_connect(int fd[2], const char *url,
+                                 const char *prog, int flags)
+{
+       char *hostandport, *path;
+       struct child_process *conn = &no_fork;
+       enum protocol protocol;
+       const char **arg;
+       struct strbuf cmd = STRBUF_INIT;
+
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
         */
-       if (protocol == PROTO_SSH && host != url)
-               port = get_port(end);
+       signal(SIGCHLD, SIG_DFL);
 
-       if (protocol == PROTO_GIT) {
+       protocol = parse_connect_url(url, &hostandport, &path);
+       if (flags & CONNECT_DIAG_URL) {
+               printf("Diag: url=%s\n", url ? url : "NULL");
+               printf("Diag: protocol=%s\n", prot_name(protocol));
+               printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
+               printf("Diag: path=%s\n", path ? path : "NULL");
+               conn = NULL;
+       } else if (protocol == PROTO_GIT) {
                /* These underlying connection commands die() if they
                 * cannot connect.
                 */
-               char *target_host = xstrdup(host);
-               if (git_use_proxy(host))
-                       conn = git_proxy_connect(fd, host);
+               char *target_host = xstrdup(hostandport);
+               if (git_use_proxy(hostandport))
+                       conn = git_proxy_connect(fd, hostandport);
                else
-                       git_tcp_connect(fd, host, flags);
+                       git_tcp_connect(fd, hostandport, flags);
                /*
                 * Separate original protocol components prog and path
                 * from extended host header with a NUL byte.
@@ -668,55 +695,51 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                             prog, path, 0,
                             target_host, 0);
                free(target_host);
-               free(url);
-               if (free_path)
-                       free(path);
-               return conn;
-       }
-
-       conn = xcalloc(1, sizeof(*conn));
-
-       strbuf_init(&cmd, MAX_CMD_LEN);
-       strbuf_addstr(&cmd, prog);
-       strbuf_addch(&cmd, ' ');
-       sq_quote_buf(&cmd, path);
-       if (cmd.len >= MAX_CMD_LEN)
-               die("command line too long");
-
-       conn->in = conn->out = -1;
-       conn->argv = arg = xcalloc(7, sizeof(*arg));
-       if (protocol == PROTO_SSH) {
-               const char *ssh = getenv("GIT_SSH");
-               int putty = ssh && strcasestr(ssh, "plink");
-               if (!ssh) ssh = "ssh";
-
-               *arg++ = ssh;
-               if (putty && !strcasestr(ssh, "tortoiseplink"))
-                       *arg++ = "-batch";
-               if (port) {
-                       /* P is for PuTTY, p is for OpenSSH */
-                       *arg++ = putty ? "-P" : "-p";
-                       *arg++ = port;
+       } else {
+               conn = xcalloc(1, sizeof(*conn));
+
+               strbuf_addstr(&cmd, prog);
+               strbuf_addch(&cmd, ' ');
+               sq_quote_buf(&cmd, path);
+
+               conn->in = conn->out = -1;
+               conn->argv = arg = xcalloc(7, sizeof(*arg));
+               if (protocol == PROTO_SSH) {
+                       const char *ssh = getenv("GIT_SSH");
+                       int putty = ssh && strcasestr(ssh, "plink");
+                       char *ssh_host = hostandport;
+                       const char *port = NULL;
+                       get_host_and_port(&ssh_host, &port);
+                       port = get_port_numeric(port);
+
+                       if (!ssh) ssh = "ssh";
+
+                       *arg++ = ssh;
+                       if (putty && !strcasestr(ssh, "tortoiseplink"))
+                               *arg++ = "-batch";
+                       if (port) {
+                               /* P is for PuTTY, p is for OpenSSH */
+                               *arg++ = putty ? "-P" : "-p";
+                               *arg++ = port;
+                       }
+                       *arg++ = ssh_host;
+               }       else {
+                       /* remove repo-local variables from the environment */
+                       conn->env = local_repo_env;
+                       conn->use_shell = 1;
                }
-               *arg++ = host;
-       }
-       else {
-               /* remove repo-local variables from the environment */
-               conn->env = local_repo_env;
-               conn->use_shell = 1;
-       }
-       *arg++ = cmd.buf;
-       *arg = NULL;
+               *arg++ = cmd.buf;
+               *arg = NULL;
 
-       if (start_command(conn))
-               die("unable to fork");
+               if (start_command(conn))
+                       die("unable to fork");
 
-       fd[0] = conn->out; /* read from child's stdout */
-       fd[1] = conn->in;  /* write to child's stdin */
-       strbuf_release(&cmd);
-       free(url);
-       if (free_path)
-               free(path);
+               fd[0] = conn->out; /* read from child's stdout */
+               fd[1] = conn->in;  /* write to child's stdin */
+               strbuf_release(&cmd);
+       }
+       free(hostandport);
+       free(path);
        return conn;
 }
 
index 64fb7dbbee8714c034c5e018b3b1b4f8c29f0ea7..c41a6850f1302e4d27af1066b53b20dd303f0d13 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -2,11 +2,13 @@
 #define CONNECT_H
 
 #define CONNECT_VERBOSE       (1u << 0)
+#define CONNECT_DIAG_URL      (1u << 1)
 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
 extern int git_connection_is_socket(struct child_process *conn);
 extern int server_supports(const char *feature);
 extern int parse_feature_request(const char *features, const char *feature);
 extern const char *server_feature_value(const char *feature, int *len_ret);
+extern int url_is_local_not_ssh(const char *url);
 
 #endif
index fae8d64c12e44cf6bcf4bab210abc4bc4b9c8ed9..51d8ba4bb7a5d04b7922a283e0ad0d904657c6b5 100644 (file)
@@ -38,7 +38,7 @@ int check_everything_connected_with_transport(sha1_iterate_fn fn,
        if (transport && transport->smart_options &&
            transport->smart_options->self_contained_and_connected &&
            transport->pack_lockfile &&
-           !suffixcmp(transport->pack_lockfile, ".keep")) {
+           ends_with(transport->pack_lockfile, ".keep")) {
                struct strbuf idx_file = STRBUF_INIT;
                strbuf_addstr(&idx_file, transport->pack_lockfile);
                strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
index 635c96bc563a21e98df181e3a179e50e0eaeadaa..2a317fca442a51dd6f47bfeeaa19bf32ed6e670f 100644 (file)
@@ -60,7 +60,7 @@
 #define gnome_keyring_memory_free gnome_keyring_free_password
 #define gnome_keyring_memory_strdup g_strdup
 
-static const chargnome_keyring_result_to_message(GnomeKeyringResult result)
+static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
 {
        switch (result) {
        case GNOME_KEYRING_RESULT_OK:
@@ -95,9 +95,9 @@ static const char* gnome_keyring_result_to_message(GnomeKeyringResult result)
 
 static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
 {
-       gpointer *data = (gpointer*) user_data;
-       int *done = (int*) data[0];
-       GnomeKeyringResult *r = (GnomeKeyringResult*) data[1];
+       gpointer *data = (gpointer *)user_data;
+       int *done = (int *)data[0];
+       GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
 
        *r = result;
        *done = 1;
@@ -130,34 +130,30 @@ static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, gu
 /*
  * This credential struct and API is simplified from git's credential.{h,c}
  */
-struct credential
-{
-       char          *protocol;
-       char          *host;
+struct credential {
+       char *protocol;
+       char *host;
        unsigned short port;
-       char          *path;
-       char          *username;
-       char          *password;
+       char *path;
+       char *username;
+       char *password;
 };
 
-#define CREDENTIAL_INIT \
-  { NULL,NULL,0,NULL,NULL,NULL }
+#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
 
-typedef int (*credential_op_cb)(struct credential*);
+typedef int (*credential_op_cb)(struct credential *);
 
-struct credential_operation
-{
-       char             *name;
+struct credential_operation {
+       char *name;
        credential_op_cb op;
 };
 
-#define CREDENTIAL_OP_END \
-  { NULL,NULL }
+#define CREDENTIAL_OP_END { NULL, NULL }
 
 /* ----------------- GNOME Keyring functions ----------------- */
 
 /* create a special keyring option string, if path is given */
-static charkeyring_object(struct credential *c)
+static char *keyring_object(struct credential *c)
 {
        if (!c->path)
                return NULL;
@@ -170,7 +166,7 @@ static char* keyring_object(struct credential *c)
 
 static int keyring_get(struct credential *c)
 {
-       charobject = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -204,7 +200,7 @@ static int keyring_get(struct credential *c)
        }
 
        /* pick the first one from the list */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        gnome_keyring_memory_free(c->password);
        c->password = gnome_keyring_memory_strdup(password_data->password);
@@ -221,7 +217,7 @@ static int keyring_get(struct credential *c)
 static int keyring_store(struct credential *c)
 {
        guint32 item_id;
-       char  *object = NULL;
+       char *object = NULL;
        GnomeKeyringResult result;
 
        /*
@@ -262,7 +258,7 @@ static int keyring_store(struct credential *c)
 
 static int keyring_erase(struct credential *c)
 {
-       char  *object = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -298,22 +294,20 @@ static int keyring_erase(struct credential *c)
        if (result == GNOME_KEYRING_RESULT_CANCELLED)
                return EXIT_SUCCESS;
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
 
        /* pick the first one from the list (delete all matches?) */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        result = gnome_keyring_item_delete_sync(
                password_data->keyring, password_data->item_id);
 
        gnome_keyring_network_password_list_free(entries);
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
@@ -325,9 +319,8 @@ static int keyring_erase(struct credential *c)
  * Table with helper operation callbacks, used by generic
  * credential helper main function.
  */
-static struct credential_operation const credential_helper_ops[] =
-{
-       { "get",   keyring_get   },
+static struct credential_operation const credential_helper_ops[] = {
+       { "get",   keyring_get },
        { "store", keyring_store },
        { "erase", keyring_erase },
        CREDENTIAL_OP_END
@@ -353,24 +346,23 @@ static void credential_clear(struct credential *c)
 
 static int credential_read(struct credential *c)
 {
-       char    *buf;
+       char *buf;
        size_t line_len;
-       char   *key;
-       char   *value;
+       char *key;
+       char *value;
 
        key = buf = gnome_keyring_memory_alloc(1024);
 
-       while (fgets(buf, 1024, stdin))
-       {
+       while (fgets(buf, 1024, stdin)) {
                line_len = strlen(buf);
 
                if (line_len && buf[line_len-1] == '\n')
-                       buf[--line_len]='\0';
+                       buf[--line_len] = '\0';
 
                if (!line_len)
                        break;
 
-               value = strchr(buf,'=');
+               value = strchr(buf, '=');
                if (!value) {
                        g_warning("invalid credential line: %s", key);
                        gnome_keyring_memory_free(buf);
@@ -384,7 +376,7 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "host")) {
                        g_free(c->host);
                        c->host = g_strdup(value);
-                       value = strrchr(c->host,':');
+                       value = strrchr(c->host, ':');
                        if (value) {
                                *value++ = '\0';
                                c->port = atoi(value);
@@ -398,7 +390,8 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "password")) {
                        gnome_keyring_memory_free(c->password);
                        c->password = gnome_keyring_memory_strdup(value);
-                       while (*value) *value++ = '\0';
+                       while (*value)
+                               *value++ = '\0';
                }
                /*
                 * Ignore other lines; we don't know what they mean, but
@@ -429,16 +422,16 @@ static void credential_write(const struct credential *c)
 static void usage(const char *name)
 {
        struct credential_operation const *try_op = credential_helper_ops;
-       const char *basename = strrchr(name,'/');
+       const char *basename = strrchr(name, '/');
 
        basename = (basename) ? basename + 1 : name;
        fprintf(stderr, "usage: %s <", basename);
        while (try_op->name) {
-               fprintf(stderr,"%s",(try_op++)->name);
+               fprintf(stderr, "%s", (try_op++)->name);
                if (try_op->name)
-                       fprintf(stderr,"%s","|");
+                       fprintf(stderr, "%s", "|");
        }
-       fprintf(stderr,"%s",">\n");
+       fprintf(stderr, "%s", ">\n");
 }
 
 int main(int argc, char *argv[])
@@ -446,7 +439,7 @@ int main(int argc, char *argv[])
        int ret = EXIT_SUCCESS;
 
        struct credential_operation const *try_op = credential_helper_ops;
-       struct credential                  cred   = CREDENTIAL_INIT;
+       struct credential cred = CREDENTIAL_INIT;
 
        if (!argv[1]) {
                usage(argv[0]);
index 7e345320ada69f1cbb909130d406cf08ce9ae130..332aba784b8810120df8e1ddfafeb034a55337a3 100755 (executable)
@@ -883,6 +883,16 @@ def main(args):
     global branches, peers
     global transports
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
 
@@ -891,7 +901,6 @@ def main(args):
     blob_marks = {}
     parsed_refs = {}
     files_cache = {}
-    marks = None
     branches = {}
     peers = {}
     transports = []
@@ -899,11 +908,8 @@ def main(args):
     if alias[5:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
     prefix = 'refs/bzr/%s' % alias
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'bzr', alias)
 
     if not is_tmp:
index 30402d55323488f7c2bc6599bbc4a471f2120b33..eb89ef67798e70fd77eff341e3dda5385d3cde48 100755 (executable)
@@ -415,6 +415,9 @@ def get_repo(url, alias):
         local_path = os.path.join(dirname, 'clone')
         if not os.path.exists(local_path):
             hg.share(myui, shared_path, local_path, update=False)
+        else:
+            # make sure the shared path is always up-to-date
+            util.writefile(os.path.join(local_path, '.hg', 'sharedpath'), hg_path)
 
         repo = hg.repository(myui, local_path)
         try:
@@ -537,7 +540,7 @@ def export_ref(repo, name, kind, head):
 
         print "commit %s" % ref
         print "mark :%d" % (note_mark)
-        print "committer remote-hg <> %s" % (ptime.strftime('%s %z'))
+        print "committer remote-hg <> %d %s" % (ptime.time(), gittz(ptime.timezone))
         desc = "Notes for %s\n" % (name)
         print "data %d" % (len(desc))
         print desc
@@ -1164,6 +1167,16 @@ def main(args):
     global dry_run
     global notes, alias
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
     peer = None
@@ -1184,16 +1197,12 @@ def main(args):
     if alias[4:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'hg', alias)
     branches = {}
     bmarks = {}
     blob_marks = {}
     parsed_refs = {}
-    marks = None
     parsed_tags = {}
     filenodes = {}
     fake_bmark = None
index 347e812923a20652b964db6b820f8f08d6c0795f..5d128a5da9a1afd3bf441f58e75d4f02a64509d5 100755 (executable)
@@ -337,6 +337,17 @@ test_expect_success 'remote cloning' '
        check gitrepo HEAD zero
 '
 
+test_expect_success 'moving remote clone' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       mv gitrepo gitrepo2 &&
+       cd gitrepo2 &&
+       git fetch
+       )
+'
+
 test_expect_success 'remote update bookmark' '
        test_when_finished "rm -rf gitrepo*" &&
 
@@ -444,6 +455,74 @@ test_expect_success 'remote new bookmark multiple branch head' '
 # cleanup previous stuff
 rm -rf hgrepo
 
+test_expect_success 'fetch special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo test >> "æ rø" &&
+       hg add "æ rø" &&
+       echo test >> "ø~?" &&
+       hg add "ø~?" &&
+       hg commit -m add-utf-8 &&
+       echo test >> "æ rø" &&
+       hg commit -m test-utf-8 &&
+       hg rm "ø~?" &&
+       hg mv "æ rø" "ø~?" &&
+       hg commit -m hg-mv-utf-8
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git -c core.quotepath=false ls-files > ../actual
+       ) &&
+       echo "ø~?" > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       mkdir -p tmp && cd tmp &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo one >> content &&
+       hg add content &&
+       hg commit -m one
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+
+       echo test >> "æ rø" &&
+       git add "æ rø" &&
+       git commit -m utf-8 &&
+
+       git push
+       ) &&
+
+       (cd hgrepo &&
+       hg update &&
+       hg manifest > ../actual
+       ) &&
+
+       printf "content\næ rø\n" > expected &&
+       test_cmp expected actual
+'
+
 setup_big_push () {
        (
        hg init hgrepo &&
index 11a95fc8b5b5eea6de2014b9209e16f9a3422186..ab80b723574ff88b6c9c4a7c98c8821600f3e6bc 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1121,7 +1121,7 @@ static int is_foreign_ident(const char *str)
 {
        int i;
 
-       if (prefixcmp(str, "$Id: "))
+       if (!starts_with(str, "$Id: "))
                return 0;
        for (i = 5; str[i]; i++) {
                if (isspace(str[i]) && str[i+1] != '$')
index 53f5375b6ca3368de6647cf5edcd7fb4dec79657..465971c7f303e29bf9501e6968da1ea71b5af76a 100644 (file)
@@ -11,7 +11,7 @@
 #include "progress.h"
 #include "csum-file.h"
 
-static void flush(struct sha1file *f, void *buf, unsigned int count)
+static void flush(struct sha1file *f, const void *buf, unsigned int count)
 {
        if (0 <= f->check_fd && count)  {
                unsigned char check_buffer[8192];
@@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
        return fd;
 }
 
-int sha1write(struct sha1file *f, void *buf, unsigned int count)
+int sha1write(struct sha1file *f, const void *buf, unsigned int count)
 {
        while (count) {
                unsigned offset = f->offset;
                unsigned left = sizeof(f->buffer) - offset;
                unsigned nr = count > left ? left : count;
-               void *data;
+               const void *data;
 
                if (f->do_crc)
                        f->crc32 = crc32(f->crc32, buf, nr);
index 3b540bdc21d2edf8d3d6fc2b5e52cc840aa19395..9dedb038ea9c77a8fadac370e039f1672e948325 100644 (file)
@@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);
 extern struct sha1file *sha1fd_check(const char *name);
 extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
 extern int sha1close(struct sha1file *, unsigned char *, unsigned int);
-extern int sha1write(struct sha1file *, void *, unsigned int);
+extern int sha1write(struct sha1file *, const void *, unsigned int);
 extern void sha1flush(struct sha1file *f);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
index 34916c5e105812e36d659dfeeb8341e003e746bd..7bee953de8e67bcb02acf178f8f1451633976b90 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -235,7 +235,7 @@ static int service_enabled;
 
 static int git_daemon_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "daemon.") &&
+       if (starts_with(var, "daemon.") &&
            !strcmp(var + 7, service_looking_at->config_name)) {
                service_enabled = git_config_bool(var, value);
                return 0;
@@ -633,7 +633,7 @@ static int execute(void)
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
                int namelen = strlen(s->name);
-               if (!prefixcmp(line, "git-") &&
+               if (starts_with(line, "git-") &&
                    !strncmp(s->name, line + 4, namelen) &&
                    line[namelen + 4] == ' ') {
                        /*
@@ -1165,11 +1165,11 @@ int main(int argc, char **argv)
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--listen=")) {
+               if (starts_with(arg, "--listen=")) {
                        string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
                        continue;
                }
-               if (!prefixcmp(arg, "--port=")) {
+               if (starts_with(arg, "--port=")) {
                        char *end;
                        unsigned long n;
                        n = strtoul(arg+7, &end, 0);
@@ -1199,19 +1199,19 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--access-hook=")) {
+               if (starts_with(arg, "--access-hook=")) {
                        access_hook = arg + 14;
                        continue;
                }
-               if (!prefixcmp(arg, "--timeout=")) {
+               if (starts_with(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        continue;
                }
-               if (!prefixcmp(arg, "--init-timeout=")) {
+               if (starts_with(arg, "--init-timeout=")) {
                        init_timeout = atoi(arg+15);
                        continue;
                }
-               if (!prefixcmp(arg, "--max-connections=")) {
+               if (starts_with(arg, "--max-connections=")) {
                        max_connections = atoi(arg+18);
                        if (max_connections < 0)
                                max_connections = 0;            /* unlimited */
@@ -1221,7 +1221,7 @@ int main(int argc, char **argv)
                        strict_paths = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--base-path=")) {
+               if (starts_with(arg, "--base-path=")) {
                        base_path = arg+12;
                        continue;
                }
@@ -1229,7 +1229,7 @@ int main(int argc, char **argv)
                        base_path_relaxed = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--interpolated-path=")) {
+               if (starts_with(arg, "--interpolated-path=")) {
                        interpolated_path = arg+20;
                        continue;
                }
@@ -1241,11 +1241,11 @@ int main(int argc, char **argv)
                        user_path = "";
                        continue;
                }
-               if (!prefixcmp(arg, "--user-path=")) {
+               if (starts_with(arg, "--user-path=")) {
                        user_path = arg + 12;
                        continue;
                }
-               if (!prefixcmp(arg, "--pid-file=")) {
+               if (starts_with(arg, "--pid-file=")) {
                        pid_file = arg + 11;
                        continue;
                }
@@ -1254,35 +1254,35 @@ int main(int argc, char **argv)
                        log_syslog = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--user=")) {
+               if (starts_with(arg, "--user=")) {
                        user_name = arg + 7;
                        continue;
                }
-               if (!prefixcmp(arg, "--group=")) {
+               if (starts_with(arg, "--group=")) {
                        group_name = arg + 8;
                        continue;
                }
-               if (!prefixcmp(arg, "--enable=")) {
+               if (starts_with(arg, "--enable=")) {
                        enable_service(arg + 9, 1);
                        continue;
                }
-               if (!prefixcmp(arg, "--disable=")) {
+               if (starts_with(arg, "--disable=")) {
                        enable_service(arg + 10, 0);
                        continue;
                }
-               if (!prefixcmp(arg, "--allow-override=")) {
+               if (starts_with(arg, "--allow-override=")) {
                        make_service_overridable(arg + 17, 1);
                        continue;
                }
-               if (!prefixcmp(arg, "--forbid-override=")) {
+               if (starts_with(arg, "--forbid-override=")) {
                        make_service_overridable(arg + 18, 0);
                        continue;
                }
-               if (!prefixcmp(arg, "--informative-errors")) {
+               if (starts_with(arg, "--informative-errors")) {
                        informative_errors = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--no-informative-errors")) {
+               if (starts_with(arg, "--no-informative-errors")) {
                        informative_errors = 0;
                        continue;
                }
index 00a8eefde9aeb96592f5ed6455dbf5d3b16747c4..33e5982a1c61a5726580f40df0a07dadc7e2aaf1 100644 (file)
@@ -183,54 +183,12 @@ static int queue_diff(struct diff_options *o,
 
 void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv,
-                  int nongit, const char *prefix)
+                  const char *prefix)
 {
        int i, prefixlen;
-       int no_index = 0;
        unsigned deprecated_show_diff_q_option_used = 0;
        const char *paths[2];
 
-       /* Were we asked to do --no-index explicitly? */
-       for (i = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(argv[i], "--no-index"))
-                       no_index = 1;
-               if (argv[i][0] != '-')
-                       break;
-       }
-
-       if (!no_index && !nongit) {
-               /*
-                * Inside a git repository, without --no-index.  Only
-                * when a path outside the repository is given,
-                * e.g. "git diff /var/tmp/[12]", or "git diff
-                * Makefile /var/tmp/Makefile", allow it to be used as
-                * a colourful "diff" replacement.
-                */
-               if ((argc != i + 2) ||
-                   (path_inside_repo(prefix, argv[i]) &&
-                    path_inside_repo(prefix, argv[i+1])))
-                       return;
-       }
-       if (argc != i + 2) {
-               if (!no_index) {
-                       /*
-                        * There was no --no-index and there were not two
-                        * paths. It is possible that the user intended
-                        * to do an inside-repository operation.
-                        */
-                       fprintf(stderr, "Not a git repository\n");
-                       fprintf(stderr,
-                               "To compare two paths outside a working tree:\n");
-               }
-               /* Give the usage message for non-repository usage and exit. */
-               usagef("git diff %s <path> <path>",
-                      no_index ? "--no-index" : "[--no-index]");
-       }
-
        diff_setup(&revs->diffopt);
        for (i = 1; i < argc - 2; ) {
                int j;
diff --git a/diff.c b/diff.c
index 3950e01910674c15d8af2c0d5d114402f43b1a9c..f99bc83b733acc7992c24c26f4465a669f7a4467 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -235,7 +235,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
        if (userdiff_config(var, value) < 0)
                return -1;
 
-       if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
+       if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                if (slot < 0)
                        return 0;
@@ -264,7 +264,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
 
        return git_default_config(var, value, cb);
@@ -1215,7 +1215,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        diff_words_append(line, len,
                                          &ecbdata->diff_words->plus);
                        return;
-               } else if (!prefixcmp(line, "\\ ")) {
+               } else if (starts_with(line, "\\ ")) {
                        /*
                         * Eat the "no newline at eof" marker as if we
                         * saw a "+" or "-" line with nothing on it,
@@ -2387,9 +2387,9 @@ static void builtin_diff(const char *name_a,
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
-               else if (!prefixcmp(diffopts, "--unified="))
+               else if (starts_with(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
-               else if (!prefixcmp(diffopts, "-u"))
+               else if (starts_with(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
@@ -2899,11 +2899,16 @@ static void run_external_diff(const char *pgm,
                              struct diff_filespec *one,
                              struct diff_filespec *two,
                              const char *xfrm_msg,
-                             int complete_rewrite)
+                             int complete_rewrite,
+                             struct diff_options *o)
 {
        const char *spawn_arg[10];
        int retval;
        const char **arg = &spawn_arg[0];
+       struct diff_queue_struct *q = &diff_queued_diff;
+       const char *env[3] = { NULL };
+       char env_counter[50];
+       char env_total[50];
 
        if (one && two) {
                struct diff_tempfile *temp_one, *temp_two;
@@ -2928,7 +2933,14 @@ static void run_external_diff(const char *pgm,
        }
        *arg = NULL;
        fflush(NULL);
-       retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
+
+       env[0] = env_counter;
+       snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
+                ++o->diff_path_counter);
+       env[1] = env_total;
+       snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+
+       retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
        if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -3042,7 +3054,7 @@ static void run_diff_cmd(const char *pgm,
 
        if (pgm) {
                run_external_diff(pgm, name, other, one, two, xfrm_msg,
-                                 complete_rewrite);
+                                 complete_rewrite, o);
                return;
        }
        if (one && two)
@@ -3317,6 +3329,8 @@ void diff_setup_done(struct diff_options *options)
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
+
+       options->diff_path_counter = 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -3391,7 +3405,7 @@ int parse_long_opt(const char *opt, const char **argv,
        if (arg[0] != '-' || arg[1] != '-')
                return 0;
        arg += strlen("--");
-       if (prefixcmp(arg, opt))
+       if (!starts_with(arg, opt))
                return 0;
        arg += strlen(opt);
        if (*arg == '=') { /* stuck form: --option=value */
@@ -3422,7 +3436,7 @@ static int stat_opt(struct diff_options *options, const char **av)
 
        switch (*arg) {
        case '-':
-               if (!prefixcmp(arg, "-width")) {
+               if (starts_with(arg, "-width")) {
                        arg += strlen("-width");
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
@@ -3432,7 +3446,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-name-width")) {
+               } else if (starts_with(arg, "-name-width")) {
                        arg += strlen("-name-width");
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
@@ -3442,7 +3456,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-graph-width")) {
+               } else if (starts_with(arg, "-graph-width")) {
                        arg += strlen("-graph-width");
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
@@ -3452,7 +3466,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-count")) {
+               } else if (starts_with(arg, "-count")) {
                        arg += strlen("-count");
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
@@ -3614,15 +3628,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
                return parse_dirstat_opt(options, "");
-       else if (!prefixcmp(arg, "-X"))
+       else if (starts_with(arg, "-X"))
                return parse_dirstat_opt(options, arg + 2);
-       else if (!prefixcmp(arg, "--dirstat="))
+       else if (starts_with(arg, "--dirstat="))
                return parse_dirstat_opt(options, arg + 10);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
        else if (!strcmp(arg, "--dirstat-by-file"))
                return parse_dirstat_opt(options, "files");
-       else if (!prefixcmp(arg, "--dirstat-by-file=")) {
+       else if (starts_with(arg, "--dirstat-by-file=")) {
                parse_dirstat_opt(options, "files");
                return parse_dirstat_opt(options, arg + 18);
        }
@@ -3639,17 +3653,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat"))
+       else if (starts_with(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
 
        /* renames options */
-       else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+       else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") ||
                 !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -B: %s", arg+2);
        }
-       else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+       else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") ||
                 !strcmp(arg, "--find-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -M: %s", arg+2);
@@ -3658,7 +3672,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
                options->irreversible_delete = 1;
        }
-       else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+       else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
                 !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
@@ -3674,7 +3688,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
-       else if (!prefixcmp(arg, "--relative=")) {
+       else if (starts_with(arg, "--relative=")) {
                DIFF_OPT_SET(options, RELATIVE_NAME);
                options->prefix = arg + 11;
        }
@@ -3727,7 +3741,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                options->use_color = 1;
-       else if (!prefixcmp(arg, "--color=")) {
+       else if (starts_with(arg, "--color=")) {
                int value = git_config_colorbool(NULL, arg+8);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
@@ -3739,7 +3753,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
-       else if (!prefixcmp(arg, "--color-words=")) {
+       else if (starts_with(arg, "--color-words=")) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
                options->word_regex = arg + 14;
@@ -3748,7 +3762,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
        }
-       else if (!prefixcmp(arg, "--word-diff=")) {
+       else if (starts_with(arg, "--word-diff=")) {
                const char *type = arg + 12;
                if (!strcmp(type, "plain"))
                        options->word_diff = DIFF_WORDS_PLAIN;
@@ -3784,12 +3798,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--ignore-submodules")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+       } else if (starts_with(arg, "--ignore-submodules=")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg + 20);
        } else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
-       else if (!prefixcmp(arg, "--submodule="))
+       else if (starts_with(arg, "--submodule="))
                return parse_submodule_opt(options, arg + 12);
 
        /* misc options */
@@ -3825,7 +3839,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (!prefixcmp(arg, "--abbrev=")) {
+       else if (starts_with(arg, "--abbrev=")) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
@@ -3907,15 +3921,15 @@ static int diff_scoreopt_parse(const char *opt)
        cmd = *opt++;
        if (cmd == '-') {
                /* convert the long-form arguments into short-form versions */
-               if (!prefixcmp(opt, "break-rewrites")) {
+               if (starts_with(opt, "break-rewrites")) {
                        opt += strlen("break-rewrites");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'B';
-               } else if (!prefixcmp(opt, "find-copies")) {
+               } else if (starts_with(opt, "find-copies")) {
                        opt += strlen("find-copies");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'C';
-               } else if (!prefixcmp(opt, "find-renames")) {
+               } else if (starts_with(opt, "find-renames")) {
                        opt += strlen("find-renames");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'M';
@@ -4325,7 +4339,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
        int new_len;
 
        /* Ignore line numbers when computing the SHA1 of the patch */
-       if (!prefixcmp(line, "@@ -"))
+       if (starts_with(line, "@@ -"))
                return;
 
        new_len = remove_space(line, len);
diff --git a/diff.h b/diff.h
index 1c05b3a6bec6ce90a97efe366614d23133026b00..ce123fa06fee6fb6e5e76d6a92ea8284a5bbbcf1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -164,6 +164,8 @@ struct diff_options {
        diff_prefix_fn_t output_prefix;
        int output_prefix_length;
        void *output_prefix_data;
+
+       int diff_path_counter;
 };
 
 enum color_diff {
@@ -330,7 +332,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
 
 extern int diff_result_code(struct diff_options *, int);
 
-extern void diff_no_index(struct rev_info *, int, const char **, int, const char *);
+extern void diff_no_index(struct rev_info *, int, const char **, const char *);
 
 extern int index_differs_from(const char *def, int diff_flags);
 
diff --git a/dir.c b/dir.c
index 23b6de47036839ca4297a9b93d1a9794547ccff1..d10a63f731020aab99f9b25a2b96a029d772410d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
+               if (pathspec->items[n].magic & PATHSPEC_EXCLUDE)
+                       continue;
                if (pathspec->items[n].magic & PATHSPEC_ICASE)
                        item_len = pathspec->items[n].prefix;
                else
@@ -279,9 +282,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
  * pathspec did not match any names, which could indicate that the
  * user mistyped the nth pathspec.
  */
-int match_pathspec_depth(const struct pathspec *ps,
-                        const char *name, int namelen,
-                        int prefix, char *seen)
+static int match_pathspec_depth_1(const struct pathspec *ps,
+                                 const char *name, int namelen,
+                                 int prefix, char *seen,
+                                 int exclude)
 {
        int i, retval = 0;
 
@@ -290,7 +294,8 @@ int match_pathspec_depth(const struct pathspec *ps,
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -309,8 +314,19 @@ int match_pathspec_depth(const struct pathspec *ps,
 
        for (i = ps->nr - 1; i >= 0; i--) {
                int how;
+
+               if ((!exclude &&   ps->items[i].magic & PATHSPEC_EXCLUDE) ||
+                   ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))
+                       continue;
+
                if (seen && seen[i] == MATCHED_EXACTLY)
                        continue;
+               /*
+                * Make exclude patterns optional and never report
+                * "pathspec ':(exclude)foo' matches no files"
+                */
+               if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE)
+                       seen[i] = MATCHED_FNMATCH;
                how = match_pathspec_item(ps->items+i, prefix, name, namelen);
                if (ps->recursive &&
                    (ps->magic & PATHSPEC_MAXDEPTH) &&
@@ -334,6 +350,18 @@ int match_pathspec_depth(const struct pathspec *ps,
        return retval;
 }
 
+int match_pathspec_depth(const struct pathspec *ps,
+                        const char *name, int namelen,
+                        int prefix, char *seen)
+{
+       int positive, negative;
+       positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);
+       if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
+               return positive;
+       negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);
+       return negative ? 0 : positive;
+}
+
 /*
  * Return the length of the "simple" part of a path match limiter.
  */
@@ -1375,11 +1403,18 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
                               PATHSPEC_MAXDEPTH |
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
-                              PATHSPEC_ICASE);
+                              PATHSPEC_ICASE |
+                              PATHSPEC_EXCLUDE);
 
        if (has_symlink_leading_path(path, len))
                return dir->nr;
 
+       /*
+        * exclude patterns are treated like positive ones in
+        * create_simplify. Usually exclude patterns should be a
+        * subset of positive ones, which has no impacts on
+        * create_simplify().
+        */
        simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        if (!len || treat_leading_path(dir, path, len, simplify))
                read_directory_recursive(dir, path, len, 0, simplify);
index 0a15349cfe38ab76b1d1cc94878cec2a8d754bbd..3c76905b9ff1eac8f627c1e7a5c034ab421ac2e4 100644 (file)
@@ -171,7 +171,7 @@ const char *get_git_namespace(void)
 
 const char *strip_namespace(const char *namespaced_ref)
 {
-       if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+       if (!starts_with(namespaced_ref, get_git_namespace()))
                return NULL;
        return namespaced_ref + namespace_len;
 }
index f4d9969e5c81f9c1415f0908f090bf62b156e179..4fd18a3574a6e72bb7a299b26b3f97fa20b8052d 100644 (file)
@@ -1877,8 +1877,8 @@ static int read_next_command(void)
                                return EOF;
 
                        if (!seen_data_command
-                               && prefixcmp(command_buf.buf, "feature ")
-                               && prefixcmp(command_buf.buf, "option ")) {
+                               && !starts_with(command_buf.buf, "feature ")
+                               && !starts_with(command_buf.buf, "option ")) {
                                parse_argv();
                        }
 
@@ -1898,7 +1898,7 @@ static int read_next_command(void)
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-               if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+               if (starts_with(command_buf.buf, "cat-blob ")) {
                        parse_cat_blob();
                        continue;
                }
@@ -1917,7 +1917,7 @@ static void skip_optional_lf(void)
 
 static void parse_mark(void)
 {
-       if (!prefixcmp(command_buf.buf, "mark :")) {
+       if (starts_with(command_buf.buf, "mark :")) {
                next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
                read_next_command();
        }
@@ -1929,10 +1929,10 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
        strbuf_reset(sb);
 
-       if (prefixcmp(command_buf.buf, "data "))
+       if (!starts_with(command_buf.buf, "data "))
                die("Expected 'data n' command, found: %s", command_buf.buf);
 
-       if (!prefixcmp(command_buf.buf + 5, "<<")) {
+       if (starts_with(command_buf.buf + 5, "<<")) {
                char *term = xstrdup(command_buf.buf + 5 + 2);
                size_t term_len = command_buf.len - 5 - 2;
 
@@ -2306,7 +2306,7 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (!prefixcmp(p, "inline ")) {
+       } else if (starts_with(p, "inline ")) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
                p += strlen("inline");  /* advance to space */
@@ -2479,7 +2479,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (!prefixcmp(p, "inline ")) {
+       } else if (starts_with(p, "inline ")) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
                p += strlen("inline");  /* advance to space */
@@ -2590,7 +2590,7 @@ static int parse_from(struct branch *b)
        const char *from;
        struct branch *s;
 
-       if (prefixcmp(command_buf.buf, "from "))
+       if (!starts_with(command_buf.buf, "from "))
                return 0;
 
        if (b->branch_tree.tree) {
@@ -2636,7 +2636,7 @@ static struct hash_list *parse_merge(unsigned int *count)
        struct branch *s;
 
        *count = 0;
-       while (!prefixcmp(command_buf.buf, "merge ")) {
+       while (starts_with(command_buf.buf, "merge ")) {
                from = strchr(command_buf.buf, ' ') + 1;
                n = xmalloc(sizeof(*n));
                s = lookup_branch(from);
@@ -2687,11 +2687,11 @@ static void parse_new_commit(void)
 
        read_next_command();
        parse_mark();
-       if (!prefixcmp(command_buf.buf, "author ")) {
+       if (starts_with(command_buf.buf, "author ")) {
                author = parse_ident(command_buf.buf + 7);
                read_next_command();
        }
-       if (!prefixcmp(command_buf.buf, "committer ")) {
+       if (starts_with(command_buf.buf, "committer ")) {
                committer = parse_ident(command_buf.buf + 10);
                read_next_command();
        }
@@ -2712,19 +2712,19 @@ static void parse_new_commit(void)
 
        /* file_change* */
        while (command_buf.len > 0) {
-               if (!prefixcmp(command_buf.buf, "M "))
+               if (starts_with(command_buf.buf, "M "))
                        file_change_m(b);
-               else if (!prefixcmp(command_buf.buf, "D "))
+               else if (starts_with(command_buf.buf, "D "))
                        file_change_d(b);
-               else if (!prefixcmp(command_buf.buf, "R "))
+               else if (starts_with(command_buf.buf, "R "))
                        file_change_cr(b, 1);
-               else if (!prefixcmp(command_buf.buf, "C "))
+               else if (starts_with(command_buf.buf, "C "))
                        file_change_cr(b, 0);
-               else if (!prefixcmp(command_buf.buf, "N "))
+               else if (starts_with(command_buf.buf, "N "))
                        note_change_n(b, &prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
-               else if (!prefixcmp(command_buf.buf, "ls "))
+               else if (starts_with(command_buf.buf, "ls "))
                        parse_ls(b);
                else {
                        unread_command_buf = 1;
@@ -2793,7 +2793,7 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* from ... */
-       if (prefixcmp(command_buf.buf, "from "))
+       if (!starts_with(command_buf.buf, "from "))
                die("Expected from command, got %s", command_buf.buf);
        from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
@@ -2821,7 +2821,7 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* tagger ... */
-       if (!prefixcmp(command_buf.buf, "tagger ")) {
+       if (starts_with(command_buf.buf, "tagger ")) {
                tagger = parse_ident(command_buf.buf + 7);
                read_next_command();
        } else
@@ -3209,7 +3209,7 @@ static void option_export_pack_edges(const char *edges)
 
 static int parse_one_option(const char *option)
 {
-       if (!prefixcmp(option, "max-pack-size=")) {
+       if (starts_with(option, "max-pack-size=")) {
                unsigned long v;
                if (!git_parse_ulong(option + 14, &v))
                        return 0;
@@ -3221,20 +3221,20 @@ static int parse_one_option(const char *option)
                        v = 1024 * 1024;
                }
                max_packsize = v;
-       } else if (!prefixcmp(option, "big-file-threshold=")) {
+       } else if (starts_with(option, "big-file-threshold=")) {
                unsigned long v;
                if (!git_parse_ulong(option + 19, &v))
                        return 0;
                big_file_threshold = v;
-       } else if (!prefixcmp(option, "depth=")) {
+       } else if (starts_with(option, "depth=")) {
                option_depth(option + 6);
-       } else if (!prefixcmp(option, "active-branches=")) {
+       } else if (starts_with(option, "active-branches=")) {
                option_active_branches(option + 16);
-       } else if (!prefixcmp(option, "export-pack-edges=")) {
+       } else if (starts_with(option, "export-pack-edges=")) {
                option_export_pack_edges(option + 18);
-       } else if (!prefixcmp(option, "quiet")) {
+       } else if (starts_with(option, "quiet")) {
                show_stats = 0;
-       } else if (!prefixcmp(option, "stats")) {
+       } else if (starts_with(option, "stats")) {
                show_stats = 1;
        } else {
                return 0;
@@ -3245,14 +3245,14 @@ static int parse_one_option(const char *option)
 
 static int parse_one_feature(const char *feature, int from_stream)
 {
-       if (!prefixcmp(feature, "date-format=")) {
+       if (starts_with(feature, "date-format=")) {
                option_date_format(feature + 12);
-       } else if (!prefixcmp(feature, "import-marks=")) {
+       } else if (starts_with(feature, "import-marks=")) {
                option_import_marks(feature + 13, from_stream, 0);
-       } else if (!prefixcmp(feature, "import-marks-if-exists=")) {
+       } else if (starts_with(feature, "import-marks-if-exists=")) {
                option_import_marks(feature + strlen("import-marks-if-exists="),
                                        from_stream, 1);
-       } else if (!prefixcmp(feature, "export-marks=")) {
+       } else if (starts_with(feature, "export-marks=")) {
                option_export_marks(feature + 13);
        } else if (!strcmp(feature, "cat-blob")) {
                ; /* Don't die - this feature is supported */
@@ -3350,7 +3350,7 @@ static void parse_argv(void)
                if (parse_one_feature(a + 2, 0))
                        continue;
 
-               if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+               if (starts_with(a + 2, "cat-blob-fd=")) {
                        option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
                        continue;
                }
@@ -3404,25 +3404,25 @@ int main(int argc, char **argv)
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
-               else if (!prefixcmp(command_buf.buf, "ls "))
+               else if (starts_with(command_buf.buf, "ls "))
                        parse_ls(NULL);
-               else if (!prefixcmp(command_buf.buf, "commit "))
+               else if (starts_with(command_buf.buf, "commit "))
                        parse_new_commit();
-               else if (!prefixcmp(command_buf.buf, "tag "))
+               else if (starts_with(command_buf.buf, "tag "))
                        parse_new_tag();
-               else if (!prefixcmp(command_buf.buf, "reset "))
+               else if (starts_with(command_buf.buf, "reset "))
                        parse_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
-               else if (!prefixcmp(command_buf.buf, "progress "))
+               else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
-               else if (!prefixcmp(command_buf.buf, "feature "))
+               else if (starts_with(command_buf.buf, "feature "))
                        parse_feature();
-               else if (!prefixcmp(command_buf.buf, "option git "))
+               else if (starts_with(command_buf.buf, "option git "))
                        parse_option();
-               else if (!prefixcmp(command_buf.buf, "option "))
+               else if (starts_with(command_buf.buf, "option "))
                        /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
index 5a1200f1a06f0ba3a9366eb9221d982d423d8da0..760ed16e79a7a46eecfb4c9efecaee7feba0f121 100644 (file)
@@ -174,9 +174,9 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
                 */
                char *line;
                while ((line = packet_read_line(fd, NULL))) {
-                       if (!prefixcmp(line, "shallow "))
+                       if (starts_with(line, "shallow "))
                                continue;
-                       if (!prefixcmp(line, "unshallow "))
+                       if (starts_with(line, "unshallow "))
                                continue;
                        die("git fetch-pack: expected shallow list");
                }
@@ -192,7 +192,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (!prefixcmp(line, "ACK ")) {
+       if (starts_with(line, "ACK ")) {
                if (!get_sha1_hex(line+4, result_sha1)) {
                        if (len < 45)
                                return ACK;
@@ -321,13 +321,13 @@ static int find_common(struct fetch_pack_args *args,
 
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (!prefixcmp(line, "shallow ")) {
+                       if (starts_with(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (!prefixcmp(line, "unshallow ")) {
+                       if (starts_with(line, "unshallow ")) {
                                if (get_sha1_hex(line + 10, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
@@ -521,7 +521,7 @@ static void filter_refs(struct fetch_pack_args *args,
                }
 
                if (!keep && args->fetch_all &&
-                   (!args->depth || prefixcmp(ref->name, "refs/tags/")))
+                   (!args->depth || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
 
                if (keep) {
index 461cbf39b2affa71e27dd1a95c24d6c65a938d62..20ccc12e57b9499b0716c8295b4adea38d8d2a86 100644 (file)
@@ -14,6 +14,7 @@ struct fetch_pack_args {
                use_thin_pack:1,
                fetch_all:1,
                stdin_refs:1,
+               diag_url:1,
                verbose:1,
                no_progress:1,
                include_tag:1,
index 71e954563d7068500bd41269dd57f01b097f7c3d..8b2da4641f8b20ff3cc70c63636de878eec72470 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -29,6 +29,17 @@ int use_gettext_poison(void)
 #endif
 
 #ifndef NO_GETTEXT
+static int test_vsnprintf(const char *fmt, ...)
+{
+       char buf[26];
+       int ret;
+       va_list ap;
+       va_start(ap, fmt);
+       ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
 static const char *charset;
 static void init_gettext_charset(const char *domain)
 {
@@ -99,9 +110,7 @@ static void init_gettext_charset(const char *domain)
           $ LANGUAGE= LANG=de_DE.utf8 ./test
           test: Kein passendes Ger?t gefunden
 
-          In the long term we should probably see about getting that
-          vsnprintf bug in glibc fixed, and audit our code so it won't
-          fall apart under a non-C locale.
+          The vsnprintf bug has been fixed since glibc 2.17.
 
           Then we could simply set LC_CTYPE from the environment, which would
           make things like the external perror(3) messages work.
@@ -115,7 +124,9 @@ static void init_gettext_charset(const char *domain)
        setlocale(LC_CTYPE, "");
        charset = locale_charset();
        bind_textdomain_codeset(domain, charset);
-       setlocale(LC_CTYPE, "C");
+       /* the string is taken from v0.99.6~1 */
+       if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0)
+               setlocale(LC_CTYPE, "C");
 }
 
 void git_setup_gettext(void)
index 7776f126d3bd4facfe987bd844a339c8a17b102a..b73916bbc64b3d2b926c4f5aa50311c7a867c711 100644 (file)
@@ -350,7 +350,9 @@ extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_lis
 extern void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void set_die_is_recursing_routine(int (*routine)(void));
 
+extern int starts_with(const char *str, const char *prefix);
 extern int prefixcmp(const char *str, const char *prefix);
+extern int ends_with(const char *str, const char *suffix);
 extern int suffixcmp(const char *str, const char *suffix);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
index b00ed95dba9c035ee812d3a2d6db2d13a1944a33..7ef36b9482ccc4111f2c888bf5ccbaa2421a5875 100755 (executable)
@@ -40,7 +40,8 @@ launch_merge_tool () {
        # the user with the real $MERGED name before launching $merge_tool.
        if should_prompt
        then
-               printf "\nViewing: '%s'\n" "$MERGED"
+               printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \
+                       "$GIT_DIFF_PATH_TOTAL" "$MERGED"
                if use_ext_cmd
                then
                        printf "Launch '%s' [Y/n]: " \
index 97716b885f8d52be465552458374698c4c78a51b..f210d0a14707c57d66baa2d098d0d2efdaaaf72e 100755 (executable)
@@ -229,15 +229,7 @@ test true = "$rebase" && {
        test -n "$curr_branch" &&
        . git-parse-remote &&
        remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
-       oldremoteref="$(git rev-parse -q --verify "$remoteref")" &&
-       for reflog in $(git rev-list -g $remoteref 2>/dev/null)
-       do
-               if test "$reflog" = "$(git merge-base $reflog $curr_branch)"
-               then
-                       oldremoteref="$reflog"
-                       break
-               fi
-       done
+       oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch)
 }
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
index 226752fbff62f4f27da95f7d711c898503fd7148..7185dc84387d6e20299fdda86800372445b9318e 100755 (executable)
@@ -14,6 +14,7 @@ git-rebase --continue | --abort | --skip | --edit-todo
 v,verbose!         display a diffstat of what changed upstream
 q,quiet!           be quiet. implies --no-stat
 autostash!         automatically stash/stash pop before and after
+fork-point         use 'merge-base --fork-point' to refine upstream
 onto=!             rebase onto given branch instead of upstream
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
@@ -66,6 +67,7 @@ verbose=
 diffstat=
 test "$(git config --bool rebase.stat)" = true && diffstat=t
 autostash="$(git config --bool rebase.autostash || echo false)"
+fork_point=auto
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -260,6 +262,12 @@ do
        --no-autosquash)
                autosquash=
                ;;
+       --fork-point)
+               fork_point=t
+               ;;
+       --no-fork-point)
+               fork_point=
+               ;;
        -M|-m)
                do_merge=t
                ;;
@@ -437,6 +445,8 @@ then
                        error_on_missing_default_upstream "rebase" "rebase" \
                                "against" "git rebase <branch>"
                fi
+
+               test "$fork_point" = auto && fork_point=t
                ;;
        *)      upstream_name="$1"
                shift
@@ -522,6 +532,15 @@ case "$#" in
        ;;
 esac
 
+if test "$fork_point" = t
+then
+       new_upstream=$(git merge-base --fork-point "$upstream_name" "$switch_to")
+       if test -n "$new_upstream"
+       then
+               upstream=$new_upstream
+       fi
+fi
+
 if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
 then
        stash_sha1=$(git stash create "autostash") ||
index 3782c3b0cb4f9356790ae581c0d408dc4f4cae75..2016d9c6198957943a8bd2510e217fe72412062a 100755 (executable)
@@ -291,7 +291,7 @@ sub signal_handler {
                    "smtp-pass:s" => \$smtp_authpass,
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
-                   "smtp-ssl-cert-path" => \$smtp_ssl_cert_path,
+                   "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
                    "smtp-debug:i" => \$debug_net_smtp,
                    "smtp-domain:s" => \$smtp_domain,
                    "identity=s" => \$identity,
@@ -1214,10 +1214,14 @@ sub send_message {
                        $smtp_server_port ||= 465; # ssmtp
                        require Net::SMTP::SSL;
                        $smtp_domain ||= maildomain();
+                       require IO::Socket::SSL;
+                       # Net::SMTP::SSL->new() does not forward any SSL options
+                       IO::Socket::SSL::set_client_defaults(
+                               ssl_verify_params());
                        $smtp ||= Net::SMTP::SSL->new($smtp_server,
                                                      Hello => $smtp_domain,
                                                      Port => $smtp_server_port,
-                                                     ssl_verify_params());
+                                                     Debug => $debug_net_smtp);
                }
                else {
                        require Net::SMTP;
diff --git a/git.c b/git.c
index 727e380e57328b1a5e5a0c719ba7a8acd912fb88..3799514ccba86834ee076c66ee1b6f8473d3ebef 100644 (file)
--- a/git.c
+++ b/git.c
@@ -54,7 +54,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                /*
                 * Check remaining flags.
                 */
-               if (!prefixcmp(cmd, "--exec-path")) {
+               if (starts_with(cmd, "--exec-path")) {
                        cmd += 11;
                        if (*cmd == '=')
                                git_set_argv_exec_path(cmd + 1);
@@ -92,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--git-dir=")) {
+               } else if (starts_with(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -106,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--namespace=")) {
+               } else if (starts_with(cmd, "--namespace=")) {
                        setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -120,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--work-tree=")) {
+               } else if (starts_with(cmd, "--work-tree=")) {
                        setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -566,7 +566,7 @@ int main(int argc, char **av)
         * So we just directly call the internal command handler, and
         * die if that one cannot handle it.
         */
-       if (!prefixcmp(cmd, "git-")) {
+       if (starts_with(cmd, "git-")) {
                cmd += 4;
                argv[0] = cmd;
                handle_internal_command(argc, argv);
@@ -578,7 +578,7 @@ int main(int argc, char **av)
        argc--;
        handle_options(&argv, &argc, NULL);
        if (argc > 0) {
-               if (!prefixcmp(argv[0], "--"))
+               if (starts_with(argv[0], "--"))
                        argv[0] += 2;
        } else {
                /* The user didn't specify a command; give them help */
index 68c77f6f8ffd120f0011eb07accdc2849afca834..3bc0f0b5ca0995910e0e517b545b5fb9af626a8b 100755 (executable)
@@ -548,6 +548,20 @@ sub evaluate_uri {
                'sub' => sub { feature_bool('remote_heads', @_) },
                'override' => 0,
                'default' => [0]},
+
+       # Enable showing branches under other refs in addition to heads
+
+       # To set system wide extra branch refs have in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'default'} = ['dirs', 'of', 'choice'];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'override'} = 1;
+       # and in project config gitweb.extrabranchrefs = dirs of choice
+       # Every directory is separated with whitespace.
+
+       'extra-branch-refs' => {
+               'sub' => \&feature_extra_branch_refs,
+               'override' => 0,
+               'default' => []},
 );
 
 sub gitweb_get_feature {
@@ -626,6 +640,21 @@ sub feature_avatar {
        return @val ? @val : @_;
 }
 
+sub feature_extra_branch_refs {
+       my (@branch_refs) = @_;
+       my $values = git_get_project_config('extrabranchrefs');
+
+       if ($values) {
+               $values = config_to_multi ($values);
+               @branch_refs = ();
+               foreach my $value (@{$values}) {
+                       push @branch_refs, split /\s+/, $value;
+               }
+       }
+
+       return @branch_refs;
+}
+
 # checking HEAD file with -e is fragile if the repository was
 # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
 # and then pruned.
@@ -656,6 +685,18 @@ sub filter_snapshot_fmts {
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
+sub filter_and_validate_refs {
+       my @refs = @_;
+       my %unique_refs = ();
+
+       foreach my $ref (@refs) {
+               die_error(500, "Invalid ref '$ref' in 'extra-branch-refs' feature") unless (is_valid_ref_format($ref));
+               # 'heads' are added implicitly in get_branch_refs().
+               $unique_refs{$ref} = 1 if ($ref ne 'heads');
+       }
+       return sort keys %unique_refs;
+}
+
 # If it is set to code reference, it is code that it is to be run once per
 # request, allowing updating configurations that change with each request,
 # while running other code in config file only once.
@@ -994,7 +1035,7 @@ sub evaluate_path_info {
 sub evaluate_and_validate_params {
        our $action = $input_params{'action'};
        if (defined $action) {
-               if (!validate_action($action)) {
+               if (!is_valid_action($action)) {
                        die_error(400, "Invalid action parameter");
                }
        }
@@ -1002,7 +1043,7 @@ sub evaluate_and_validate_params {
        # parameters which are pathnames
        our $project = $input_params{'project'};
        if (defined $project) {
-               if (!validate_project($project)) {
+               if (!is_valid_project($project)) {
                        undef $project;
                        die_error(404, "No such project");
                }
@@ -1010,21 +1051,21 @@ sub evaluate_and_validate_params {
 
        our $project_filter = $input_params{'project_filter'};
        if (defined $project_filter) {
-               if (!validate_pathname($project_filter)) {
+               if (!is_valid_pathname($project_filter)) {
                        die_error(404, "Invalid project_filter parameter");
                }
        }
 
        our $file_name = $input_params{'file_name'};
        if (defined $file_name) {
-               if (!validate_pathname($file_name)) {
+               if (!is_valid_pathname($file_name)) {
                        die_error(400, "Invalid file parameter");
                }
        }
 
        our $file_parent = $input_params{'file_parent'};
        if (defined $file_parent) {
-               if (!validate_pathname($file_parent)) {
+               if (!is_valid_pathname($file_parent)) {
                        die_error(400, "Invalid file parent parameter");
                }
        }
@@ -1032,21 +1073,21 @@ sub evaluate_and_validate_params {
        # parameters which are refnames
        our $hash = $input_params{'hash'};
        if (defined $hash) {
-               if (!validate_refname($hash)) {
+               if (!is_valid_refname($hash)) {
                        die_error(400, "Invalid hash parameter");
                }
        }
 
        our $hash_parent = $input_params{'hash_parent'};
        if (defined $hash_parent) {
-               if (!validate_refname($hash_parent)) {
+               if (!is_valid_refname($hash_parent)) {
                        die_error(400, "Invalid hash parent parameter");
                }
        }
 
        our $hash_base = $input_params{'hash_base'};
        if (defined $hash_base) {
-               if (!validate_refname($hash_base)) {
+               if (!is_valid_refname($hash_base)) {
                        die_error(400, "Invalid hash base parameter");
                }
        }
@@ -1066,7 +1107,7 @@ sub evaluate_and_validate_params {
 
        our $hash_parent_base = $input_params{'hash_parent_base'};
        if (defined $hash_parent_base) {
-               if (!validate_refname($hash_parent_base)) {
+               if (!is_valid_refname($hash_parent_base)) {
                        die_error(400, "Invalid hash parent base parameter");
                }
        }
@@ -1113,7 +1154,7 @@ sub evaluate_git_dir {
        our $git_dir = "$projectroot/$project" if $project;
 }
 
-our (@snapshot_fmts, $git_avatar);
+our (@snapshot_fmts, $git_avatar, @extra_branch_refs);
 sub configure_gitweb_features {
        # list of supported snapshot formats
        our @snapshot_fmts = gitweb_get_feature('snapshot');
@@ -1131,6 +1172,13 @@ sub configure_gitweb_features {
        } else {
                $git_avatar = '';
        }
+
+       our @extra_branch_refs = gitweb_get_feature('extra-branch-refs');
+       @extra_branch_refs = filter_and_validate_refs (@extra_branch_refs);
+}
+
+sub get_branch_refs {
+       return ('heads', @extra_branch_refs);
 }
 
 # custom error handler: 'die <message>' is Internal Server Error
@@ -1418,27 +1466,30 @@ sub href {
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
-sub validate_action {
-       my $input = shift || return undef;
+sub is_valid_action {
+       my $input = shift;
        return undef unless exists $actions{$input};
-       return $input;
+       return 1;
 }
 
-sub validate_project {
-       my $input = shift || return undef;
-       if (!validate_pathname($input) ||
+sub is_valid_project {
+       my $input = shift;
+
+       return unless defined $input;
+       if (!is_valid_pathname($input) ||
                !(-d "$projectroot/$input") ||
                !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
-               return $input;
+               return 1;
        }
 }
 
-sub validate_pathname {
-       my $input = shift || return undef;
+sub is_valid_pathname {
+       my $input = shift;
 
+       return undef unless defined $input;
        # no '.' or '..' as elements of path, i.e. no '.' nor '..'
        # at the beginning, at the end, and between slashes.
        # also this catches doubled slashes
@@ -1449,24 +1500,33 @@ sub validate_pathname {
        if ($input =~ m!\0!) {
                return undef;
        }
-       return $input;
+       return 1;
 }
 
-sub validate_refname {
-       my $input = shift || return undef;
+sub is_valid_ref_format {
+       my $input = shift;
 
-       # textual hashes are O.K.
-       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
-               return $input;
-       }
-       # it must be correct pathname
-       $input = validate_pathname($input)
-               or return undef;
+       return undef unless defined $input;
        # restrictions on ref name according to git-check-ref-format
        if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
                return undef;
        }
-       return $input;
+       return 1;
+}
+
+sub is_valid_refname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # textual hashes are O.K.
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return 1;
+       }
+       # it must be correct pathname
+       is_valid_pathname($input) or return undef;
+       # check git-check-ref-format restrictions
+       is_valid_ref_format($input) or return undef;
+       return 1;
 }
 
 # decode sequences of octets in utf8 into Perl's internal form,
@@ -2515,6 +2575,7 @@ sub format_snapshot_links {
 sub get_feed_info {
        my $format = shift || 'Atom';
        my %res = (action => lc($format));
+       my $matched_ref = 0;
 
        # feed links are possible only for project views
        return unless (defined $project);
@@ -2522,12 +2583,17 @@ sub get_feed_info {
        # or don't have specific feed yet (so they should use generic)
        return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x);
 
-       my $branch;
-       # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
-       # from tag links; this also makes possible to detect branch links
-       if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
-           (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
-               $branch = $1;
+       my $branch = undef;
+       # branches refs uses 'refs/' + $get_branch_refs()[x] + '/' prefix
+       # (fullname) to differentiate from tag links; this also makes
+       # possible to detect branch links
+       for my $ref (get_branch_refs()) {
+               if ((defined $hash_base && $hash_base =~ m!^refs/\Q$ref\E/(.*)$!) ||
+                   (defined $hash      && $hash      =~ m!^refs/\Q$ref\E/(.*)$!)) {
+                       $branch = $1;
+                       $matched_ref = $ref;
+                       last;
+               }
        }
        # find log type for feed description (title)
        my $type = 'log';
@@ -2540,7 +2606,7 @@ sub get_feed_info {
        }
 
        $res{-title} = $type;
-       $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
+       $res{'hash'} = (defined $branch ? "refs/$matched_ref/$branch" : undef);
        $res{'file_name'} = $file_name;
 
        return %res;
@@ -3193,7 +3259,7 @@ sub git_get_last_activity {
             '--format=%(committer)',
             '--sort=-committerdate',
             '--count=1',
-            'refs/heads') or return;
+            map { "refs/$_" } get_branch_refs ()) or return;
        my $most_recent = <$fd>;
        close $fd or return;
        if (defined $most_recent &&
@@ -3644,7 +3710,7 @@ sub parse_from_to_diffinfo {
 
 sub git_get_heads_list {
        my ($limit, @classes) = @_;
-       @classes = ('heads') unless @classes;
+       @classes = get_branch_refs() unless @classes;
        my @patterns = map { "refs/$_" } @classes;
        my @headslist;
 
@@ -3662,9 +3728,16 @@ sub git_get_heads_list {
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
                $ref_item{'fullname'}  = $name;
-               $name =~ s!^refs/(?:head|remote)s/!!;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               $name =~ s!^refs/($strip_refs|remotes)/!!;
+               $ref_item{'name'} = $name;
+               # for refs neither in 'heads' nor 'remotes' we want to
+               # show their ref dir
+               my $ref_dir = (defined $1) ? $1 : '';
+               if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                   $ref_item{'name'} .= ' (' . $ref_dir . ')';
+               }
 
-               $ref_item{'name'}  = $name;
                $ref_item{'id'}    = $hash;
                $ref_item{'title'} = $title || '(no commit message)';
                $ref_item{'epoch'} = $epoch;
@@ -7156,6 +7229,15 @@ sub git_tree {
        git_footer_html();
 }
 
+sub sanitize_for_filename {
+    my $name = shift;
+
+    $name =~ s!/!-!g;
+    $name =~ s/[^[:alnum:]_.-]//g;
+
+    return $name;
+}
+
 sub snapshot_name {
        my ($project, $hash) = @_;
 
@@ -7163,9 +7245,7 @@ sub snapshot_name {
        # path/to/project/.git -> project
        my $name = to_utf8($project);
        $name =~ s,([^/])/*\.git$,$1,;
-       $name = basename($name);
-       # sanitize name
-       $name =~ s/[[:cntrl:]]/?/g;
+       $name = sanitize_for_filename(basename($name));
 
        my $ver = $hash;
        if ($hash =~ /^[0-9a-fA-F]+$/) {
@@ -7179,13 +7259,25 @@ sub snapshot_name {
                $ver = $1;
        } else {
                # branches and other need shortened SHA-1 hash
-               if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
-                       $ver = $1;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               if ($hash =~ m!^refs/($strip_refs|remotes)/(.*)$!) {
+                       my $ref_dir = (defined $1) ? $1 : '';
+                       $ver = $2;
+
+                       $ref_dir = sanitize_for_filename($ref_dir);
+                       # for refs neither in heads nor remotes we want to
+                       # add a ref dir to archive name
+                       if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                               $ver = $ref_dir . '-' . $ver;
+                       }
                }
                $ver .= '-' . git_get_short_hash($project, $hash);
        }
+       # special case of sanitization for filename - we change
+       # slashes to dots instead of dashes
        # in case of hierarchical branch names
        $ver =~ s!/!.!g;
+       $ver =~ s/[^[:alnum:]_.-]//g;
 
        # name = project-version_string
        $name = "$name-$ver";
diff --git a/help.c b/help.c
index f068925bb7f26055fb699cda7173a0c06ac46a22..df7d16d7ce52ce9b9f274db92b24aa64bac56ebf 100644 (file)
--- a/help.c
+++ b/help.c
@@ -148,7 +148,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
        while ((de = readdir(dir)) != NULL) {
                int entlen;
 
-               if (prefixcmp(de->d_name, prefix))
+               if (!starts_with(de->d_name, prefix))
                        continue;
 
                strbuf_setlen(&buf, len);
@@ -255,7 +255,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "help.autocorrect"))
                autocorrect = git_config_int(var,value);
        /* Also use aliases for command lookup */
-       if (!prefixcmp(var, "alias."))
+       if (starts_with(var, "alias."))
                add_cmdname(&aliases, var + 6, strlen(var + 6));
 
        return git_default_config(var, value, cb);
@@ -329,7 +329,7 @@ const char *help_unknown_cmd(const char *cmd)
                if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
                        /* Yes, this is one of the common commands */
                        n++; /* use the entry from common_cmds[] */
-                       if (!prefixcmp(candidate, cmd)) {
+                       if (starts_with(candidate, cmd)) {
                                /* Give prefix match a very good score */
                                main_cmds.names[i]->len = 0;
                                continue;
@@ -414,7 +414,7 @@ static int append_similar_ref(const char *refname, const unsigned char *sha1,
        struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
        char *branch = strrchr(refname, '/') + 1;
        /* A remote branch of the same name is deemed similar */
-       if (!prefixcmp(refname, "refs/remotes/") &&
+       if (starts_with(refname, "refs/remotes/") &&
            !strcmp(branch, cb->base_ref))
                string_list_append(cb->similar_refs,
                                   refname + strlen("refs/remotes/"));
index 8c464bd8051ed813175b01f832e5e78a91bba8ea..d2c0a625cef558df252af6927cd3a86424674862 100644 (file)
@@ -226,7 +226,7 @@ static int http_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "http.")) {
+       if (starts_with(var, "http.")) {
                int i;
 
                for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
@@ -247,7 +247,7 @@ static struct rpc_service *select_service(const char *name)
        struct rpc_service *svc = NULL;
        int i;
 
-       if (prefixcmp(name, "git-"))
+       if (!starts_with(name, "git-"))
                forbidden("Unsupported service: '%s'", name);
 
        for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
index 34cb70f90ea14a18848bd270463f5a34dc2c8fd3..d4b40c9c60e8caa2b8f8e676ac18b678b6ca2ed6 100644 (file)
@@ -771,7 +771,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
                        lock->owner = xmalloc(strlen(ctx->cdata) + 1);
                        strcpy(lock->owner, ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
-                       if (!prefixcmp(ctx->cdata, "Second-"))
+                       if (starts_with(ctx->cdata, "Second-"))
                                lock->timeout =
                                        strtol(ctx->cdata + 7, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
@@ -1579,7 +1579,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
                return;
 
        /* If it's a symref, set the refname; otherwise try for a sha1 */
-       if (!prefixcmp((char *)buffer.buf, "ref: ")) {
+       if (starts_with((char *)buffer.buf, "ref: ")) {
                *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
        } else {
                get_sha1_hex(buffer.buf, sha1);
diff --git a/http.c b/http.c
index ccb813b86fb47dac7dcb3a1eb86faaa9e8ecc923..70eaa26e88cbcfa62e7d5e967b4e519432319210 100644 (file)
--- a/http.c
+++ b/http.c
@@ -460,7 +460,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
                credential_from_url(&http_auth, url);
                if (!ssl_cert_password_required &&
                    getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
-                   !prefixcmp(url, "https://"))
+                   starts_with(url, "https://"))
                        ssl_cert_password_required = 1;
        }
 
@@ -1000,7 +1000,7 @@ static int update_url_from_redirect(struct strbuf *base,
        if (!strcmp(asked, got->buf))
                return 0;
 
-       if (prefixcmp(asked, base->buf))
+       if (!starts_with(asked, base->buf))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
@@ -1106,7 +1106,7 @@ int http_fetch_ref(const char *base, struct ref *ref)
                strbuf_rtrim(&buffer);
                if (buffer.len == 40)
                        ret = get_sha1_hex(buffer.buf, ref->old_sha1);
-               else if (!prefixcmp(buffer.buf, "ref: ")) {
+               else if (starts_with(buffer.buf, "ref: ")) {
                        ref->symref = xstrdup(buffer.buf + 5);
                        ret = 0;
                }
@@ -1207,8 +1207,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
                case 'P':
                        i++;
                        if (i + 52 <= buf.len &&
-                           !prefixcmp(data + i, " pack-") &&
-                           !prefixcmp(data + i + 46, ".pack\n")) {
+                           starts_with(data + i, " pack-") &&
+                           starts_with(data + i + 46, ".pack\n")) {
                                get_sha1_hex(data + i + 6, sha1);
                                fetch_and_setup_pack_index(packs_head, sha1,
                                                      base_url);
index 6f5cc4f782ea82fc14ee596c7bac96e2b6f907e7..0bc6f7fae151ea364e9d141b30b8e68d544a4448 100644 (file)
@@ -1263,7 +1263,7 @@ static int count_messages(struct strbuf *all_msgs)
        char *p = all_msgs->buf;
 
        while (1) {
-               if (!prefixcmp(p, "From ")) {
+               if (starts_with(p, "From ")) {
                        p = strstr(p+5, "\nFrom: ");
                        if (!p) break;
                        p = strstr(p+7, "\nDate: ");
@@ -1297,7 +1297,7 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
        data = &all_msgs->buf[*ofs];
        len = all_msgs->len - *ofs;
 
-       if (len < 5 || prefixcmp(data, "From "))
+       if (len < 5 || !starts_with(data, "From "))
                return 0;
 
        p = strchr(data, '\n');
@@ -1339,13 +1339,13 @@ static int git_imap_config(const char *key, const char *val, void *cb)
        if (!strcmp("folder", key)) {
                imap_folder = xstrdup(val);
        } else if (!strcmp("host", key)) {
-               if (!prefixcmp(val, "imap:"))
+               if (starts_with(val, "imap:"))
                        val += 5;
-               else if (!prefixcmp(val, "imaps:")) {
+               else if (starts_with(val, "imaps:")) {
                        val += 6;
                        server.use_ssl = 1;
                }
-               if (!prefixcmp(val, "//"))
+               if (starts_with(val, "//"))
                        val += 2;
                server.host = xstrdup(val);
        } else if (!strcmp("user", key))
index e958d0748f11d82995f63d06b7e6ed05b2e7e4f1..642faffdb7d149f162c8716eb13dd878d16baa4b 100644 (file)
@@ -98,7 +98,7 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        struct object *obj;
        enum decoration_type type = DECORATION_NONE;
 
-       if (!prefixcmp(refname, "refs/replace/")) {
+       if (starts_with(refname, "refs/replace/")) {
                unsigned char original_sha1[20];
                if (!read_replace_refs)
                        return 0;
@@ -116,11 +116,11 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        if (!obj)
                return 0;
 
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                type = DECORATION_REF_LOCAL;
-       else if (!prefixcmp(refname, "refs/remotes/"))
+       else if (starts_with(refname, "refs/remotes/"))
                type = DECORATION_REF_REMOTE;
-       else if (!prefixcmp(refname, "refs/tags/"))
+       else if (starts_with(refname, "refs/tags/"))
                type = DECORATION_REF_TAG;
        else if (!strcmp(refname, "refs/stash"))
                type = DECORATION_REF_STASH;
index dbb7104c043b14f2a43d67e1ca87ce88f4a55408..a18bd15dd3dcf303909b4b541f35815705a3f50b 100644 (file)
@@ -2063,13 +2063,13 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
-       else if (!prefixcmp(s, "subtree="))
+       else if (starts_with(s, "subtree="))
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
-       else if (!prefixcmp(s, "diff-algorithm=")) {
+       else if (starts_with(s, "diff-algorithm=")) {
                long value = parse_algorithm_value(s + strlen("diff-algorithm="));
                if (value < 0)
                        return -1;
@@ -2088,7 +2088,7 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
-       else if (!prefixcmp(s, "rename-threshold=")) {
+       else if (starts_with(s, "rename-threshold=")) {
                const char *score = s + strlen("rename-threshold=");
                if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
                        return -1;
index 7bb3473dbe44c1f8325ee6891620eec67163dfcd..2975dcd581024cd483953b47636466b79c95bafe 100644 (file)
@@ -70,7 +70,7 @@ static combine_notes_fn parse_combine_notes_fn(const char *v)
 static int notes_rewrite_config(const char *k, const char *v, void *cb)
 {
        struct notes_rewrite_cfg *c = cb;
-       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+       if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
                c->enabled = git_config_bool(k, v);
                return 0;
        } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
@@ -85,7 +85,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb)
        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
                /* note that a refs/ prefix is implied in the
                 * underlying for_each_glob_ref */
-               if (!prefixcmp(v, "refs/notes/"))
+               if (starts_with(v, "refs/notes/"))
                        string_list_add_refs_by_glob(c->refs, v);
                else
                        warning(_("Refusing to rewrite notes in %s"
diff --git a/notes.c b/notes.c
index b69c0b82577e0d4958ddf3efbb4aec5a9c5fa99e..5f07c0b7f28e09bd2f7969ea08b00b319a226920 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1243,9 +1243,9 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
                if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
                        strbuf_addstr(sb, "\nNotes:\n");
                } else {
-                       if (!prefixcmp(ref, "refs/"))
+                       if (starts_with(ref, "refs/"))
                                ref += 5;
-                       if (!prefixcmp(ref, "notes/"))
+                       if (starts_with(ref, "notes/"))
                                ref += 6;
                        strbuf_addf(sb, "\nNotes (%s):\n", ref);
                }
@@ -1293,9 +1293,9 @@ int copy_note(struct notes_tree *t,
 
 void expand_notes_ref(struct strbuf *sb)
 {
-       if (!prefixcmp(sb->buf, "refs/notes/"))
+       if (starts_with(sb->buf, "refs/notes/"))
                return; /* we're happy */
-       else if (!prefixcmp(sb->buf, "notes/"))
+       else if (starts_with(sb->buf, "notes/"))
                strbuf_insert(sb, 0, "refs/", 5);
        else
                strbuf_insert(sb, 0, "refs/notes/", 11);
index ca9e63be18f9333bf0f603ddb9ddd923588be0aa..ddc174e1adb9e1545b2815d8a1990e43f6fa2bfb 100644 (file)
@@ -44,14 +44,13 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
  */
 const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
                           int nr_objects, const struct pack_idx_option *opts,
-                          unsigned char *sha1)
+                          const unsigned char *sha1)
 {
        struct sha1file *f;
        struct pack_idx_entry **sorted_by_sha, **list, **last;
        off_t last_obj_offset = 0;
        uint32_t array[256];
        int i, fd;
-       git_SHA_CTX ctx;
        uint32_t index_version;
 
        if (nr_objects) {
@@ -114,9 +113,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        }
        sha1write(f, array, 256 * 4);
 
-       /* compute the SHA1 hash of sorted object names. */
-       git_SHA1_Init(&ctx);
-
        /*
         * Write the actual SHA1 entries..
         */
@@ -128,7 +124,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                        sha1write(f, &offset, 4);
                }
                sha1write(f, obj->sha1, 20);
-               git_SHA1_Update(&ctx, obj->sha1, 20);
                if ((opts->flags & WRITE_IDX_STRICT) &&
                    (i && !hashcmp(list[-2]->sha1, obj->sha1)))
                        die("The same object %s appears twice in the pack",
@@ -178,7 +173,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        sha1write(f, sha1, 20);
        sha1close(f, NULL, ((opts->flags & WRITE_IDX_VERIFY)
                            ? CSUM_CLOSE : CSUM_FSYNC));
-       git_SHA1_Final(sha1, &ctx);
        return index_name;
 }
 
diff --git a/pack.h b/pack.h
index aa6ee7d606f1d5336bff9a3067b44057c362e788..12d951659c16462cf2da6a43e2c4974b85e39712 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -76,7 +76,7 @@ struct pack_idx_entry {
 struct progress;
 typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
 
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1);
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 extern int verify_pack_index(struct packed_git *);
 extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
diff --git a/pager.c b/pager.c
index fa19765eb9e60d2dbc17887773fff4ad66a32c37..345b0bc4b58ef641a653d6244d2ef7558fc84347 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -151,7 +151,7 @@ int decimal_width(int number)
 static int pager_command_config(const char *var, const char *value, void *data)
 {
        struct pager_config *c = data;
-       if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
+       if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) {
                int b = git_config_maybe_bool(var, value);
                if (b >= 0)
                        c->want = b;
index 62e9b1cc6831768315dd74eac30348668de22da3..7b8d3faa171309e84bff23c70206a9c8bd99cc71 100644 (file)
@@ -273,13 +273,13 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        if (options->flags & PARSE_OPT_NONEG)
                                continue;
                        /* negated and abbreviated very much? */
-                       if (!prefixcmp("no-", arg)) {
+                       if (starts_with("no-", arg)) {
                                flags |= OPT_UNSET;
                                goto is_abbreviated;
                        }
                        /* negated? */
-                       if (prefixcmp(arg, "no-")) {
-                               if (!prefixcmp(long_name, "no-")) {
+                       if (!starts_with(arg, "no-")) {
+                               if (starts_with(long_name, "no-")) {
                                        long_name += 3;
                                        opt_flags |= OPT_UNSET;
                                        goto again;
@@ -289,7 +289,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        flags |= OPT_UNSET;
                        rest = skip_prefix(arg + 3, long_name);
                        /* abbreviated and negated? */
-                       if (!rest && !prefixcmp(long_name, arg + 3))
+                       if (!rest && starts_with(long_name, arg + 3))
                                goto is_abbreviated;
                        if (!rest)
                                continue;
@@ -334,7 +334,7 @@ static void check_typos(const char *arg, const struct option *options)
        if (strlen(arg) < 3)
                return;
 
-       if (!prefixcmp(arg, "no-")) {
+       if (starts_with(arg, "no-")) {
                error ("did you mean `--%s` (with two dashes ?)", arg);
                exit(129);
        }
@@ -342,7 +342,7 @@ static void check_typos(const char *arg, const struct option *options)
        for (; options->type != OPTION_END; options++) {
                if (!options->long_name)
                        continue;
-               if (!prefixcmp(options->long_name, arg)) {
+               if (starts_with(options->long_name, arg)) {
                        error ("did you mean `--%s` (with two dashes ?)", arg);
                        exit(129);
                }
index 8736006ed73560ebb05abac2debb04d5117dc114..d670cb9669f213951b9b2cac332c7869be6ca2ee 100644 (file)
@@ -161,9 +161,6 @@ struct option {
          N_("no-op (backward compatibility)"),         \
          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
 
-/* Deprecated synonym */
-#define OPT_BOOLEAN OPT_COUNTUP
-
 /* parse_options() will filter out the processed options and leave the
  * non-option arguments in argv[]. usagestr strings should be marked
  * for translation with N_().
@@ -232,15 +229,15 @@ extern int parse_opt_tertiary(const struct option *, const char *, int);
 extern int parse_opt_string_list(const struct option *, const char *, int);
 extern int parse_opt_noop_cb(const struct option *, const char *, int);
 
-#define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
-#define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
+#define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', "verbose", (var), (h))
+#define OPT__QUIET(var, h)    OPT_COUNTUP('q', "quiet",   (var), (h))
 #define OPT__VERBOSITY(var) \
        { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
        { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
-#define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
-#define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
+#define OPT__DRY_RUN(var, h)  OPT_BOOL('n', "dry-run", (var), (h))
+#define OPT__FORCE(var, h)    OPT_COUNTUP('f', "force",   (var), (h))
 #define OPT__ABBREV(var)  \
        { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
          N_("use <n> digits to display SHA-1s"),       \
index 87b3b82f1fd9baee7758159e5e3119b6da0655e3..6cb9fd3014a424d11647e48de3b841012ccc2ffe 100644 (file)
@@ -71,8 +71,23 @@ static struct pathspec_magic {
        { PATHSPEC_LITERAL,   0, "literal" },
        { PATHSPEC_GLOB,   '\0', "glob" },
        { PATHSPEC_ICASE,  '\0', "icase" },
+       { PATHSPEC_EXCLUDE, '!', "exclude" },
 };
 
+static void prefix_short_magic(struct strbuf *sb, int prefixlen,
+                              unsigned short_magic)
+{
+       int i;
+       strbuf_addstr(sb, ":(");
+       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+               if (short_magic & pathspec_magic[i].bit) {
+                       if (sb->buf[sb->len - 1] != '(')
+                               strbuf_addch(sb, ',');
+                       strbuf_addstr(sb, pathspec_magic[i].name);
+               }
+       strbuf_addf(sb, ",prefix:%d)", prefixlen);
+}
+
 /*
  * Take an element of a pathspec and check for magic signatures.
  * Append the result to the prefix. Return the magic bitmap.
@@ -154,7 +169,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                                        magic |= pathspec_magic[i].bit;
                                        break;
                                }
-                               if (!prefixcmp(copyfrom, "prefix:")) {
+                               if (starts_with(copyfrom, "prefix:")) {
                                        char *endptr;
                                        pathspec_prefix = strtol(copyfrom + 7,
                                                                 &endptr, 10);
@@ -232,22 +247,16 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
         */
        if (flags & PATHSPEC_PREFIX_ORIGIN) {
                struct strbuf sb = STRBUF_INIT;
-               const char *start = elt;
                if (prefixlen && !literal_global) {
                        /* Preserve the actual prefix length of each pattern */
                        if (short_magic)
-                               die("BUG: prefixing on short magic is not supported");
+                               prefix_short_magic(&sb, prefixlen, short_magic);
                        else if (long_magic_end) {
-                               strbuf_add(&sb, start, long_magic_end - start);
-                               strbuf_addf(&sb, ",prefix:%d", prefixlen);
-                               start = long_magic_end;
-                       } else {
-                               if (*start == ':')
-                                       start++;
+                               strbuf_add(&sb, elt, long_magic_end - elt);
+                               strbuf_addf(&sb, ",prefix:%d)", prefixlen);
+                       } else
                                strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
-                       }
                }
-               strbuf_add(&sb, start, copyfrom - start);
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
        } else
@@ -355,7 +364,7 @@ void parse_pathspec(struct pathspec *pathspec,
 {
        struct pathspec_item *item;
        const char *entry = argv ? *argv : NULL;
-       int i, n, prefixlen;
+       int i, n, prefixlen, nr_exclude = 0;
 
        memset(pathspec, 0, sizeof(*pathspec));
 
@@ -412,6 +421,8 @@ void parse_pathspec(struct pathspec *pathspec,
                if ((flags & PATHSPEC_LITERAL_PATH) &&
                    !(magic_mask & PATHSPEC_LITERAL))
                        item[i].magic |= PATHSPEC_LITERAL;
+               if (item[i].magic & PATHSPEC_EXCLUDE)
+                       nr_exclude++;
                if (item[i].magic & magic_mask)
                        unsupported_magic(entry,
                                          item[i].magic & magic_mask,
@@ -427,6 +438,10 @@ void parse_pathspec(struct pathspec *pathspec,
                pathspec->magic |= item[i].magic;
        }
 
+       if (nr_exclude == n)
+               die(_("There is nothing to exclude from by :(exclude) patterns.\n"
+                     "Perhaps you forgot to add either ':/' or '.' ?"));
+
 
        if (pathspec->magic & PATHSPEC_MAXDEPTH) {
                if (flags & PATHSPEC_KEEP_ORDER)
index a75e9242d1bea7e9318426d6786bd5eae9dc6215..0c1126264a394e62cfbed47b2feae85e9b5d1410 100644 (file)
@@ -7,12 +7,14 @@
 #define PATHSPEC_LITERAL       (1<<2)
 #define PATHSPEC_GLOB          (1<<3)
 #define PATHSPEC_ICASE         (1<<4)
+#define PATHSPEC_EXCLUDE       (1<<5)
 #define PATHSPEC_ALL_MAGIC       \
        (PATHSPEC_FROMTOP       | \
         PATHSPEC_MAXDEPTH      | \
         PATHSPEC_LITERAL       | \
         PATHSPEC_GLOB          | \
-        PATHSPEC_ICASE)
+        PATHSPEC_ICASE         | \
+        PATHSPEC_EXCLUDE)
 
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
index b3bcd476daa7d4ab32c30c6fbd2f0a73ec9d0b4f..34e8af966c4a97bd3fafd5d9cfecbfb982f78897 100644 (file)
@@ -304,8 +304,12 @@ sub C {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend (v1.8.5 and below):
+       # store third argument to ->add_file() in a local variable, to make it
+       # have the same lifetime as $fbat
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->chg_file($fbat, $m);
        $self->close_file($fbat,undef,$self->{pool});
@@ -323,8 +327,10 @@ sub R {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend, see comment in C() above
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->apply_autoprops($file, $fbat);
        $self->chg_file($fbat, $m);
index 70f19501d0e0c0dd625976519fd039d5c7a8b314..bc63b3b80e5b2853f920ba66fe977c7cc0921c78 100644 (file)
@@ -24,8 +24,8 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        strbuf_addf(&out, "packet: %12s%c ",
                    packet_trace_prefix, write ? '>' : '<');
 
-       if ((len >= 4 && !prefixcmp(buf, "PACK")) ||
-           (len >= 5 && !prefixcmp(buf+1, "PACK"))) {
+       if ((len >= 4 && starts_with(buf, "PACK")) ||
+           (len >= 5 && starts_with(buf+1, "PACK"))) {
                strbuf_addstr(&out, "PACK ...");
                unsetenv(trace_key);
        }
index 5e2d709e152d930fbc01633511241b404d2ce252..c0bbb65bf6aa3066da667aa5c69058f0e6b5ded2 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -72,7 +72,7 @@ msgstr "Archivformat"
 
 #: archive.c:328 builtin/log.c:1193
 msgid "prefix"
-msgstr "Prefix"
+msgstr "Präfix"
 
 #: archive.c:329
 msgid "prepend prefix to each pathname in the archive"
@@ -3716,7 +3716,7 @@ msgid ""
 msgstr ""
 "Eingabehilfe:\n"
 "1          - nummeriertes Element auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "           - (leer) nichts auswählen"
 
 #: builtin/clean.c:298
@@ -3734,7 +3734,7 @@ msgstr ""
 "1          - einzelnes Element auswählen\n"
 "3-5        - Bereich von Elementen auswählen\n"
 "2-3,6-9    - mehrere Bereiche auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "-...       - angegebenes Element abwählen\n"
 "*          - alle Elemente auswählen\n"
 "           - (leer) Auswahl beenden"
@@ -6452,7 +6452,7 @@ msgstr "kennzeichnet die Serie als n-te Fassung"
 
 #: builtin/log.c:1194
 msgid "Use [<prefix>] instead of [PATCH]"
-msgstr "verwendet [<Prefix>] anstatt [PATCH]"
+msgstr "verwendet [<Präfix>] anstatt [PATCH]"
 
 #: builtin/log.c:1197
 msgid "store resulting files in <dir>"
@@ -8182,7 +8182,7 @@ msgid ""
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Prefix>] "
+"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Präfix>] "
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<Datei>] (--empty | <Commit-Referenz1> [<Commit-Referenz2> "
 "[<Commit-Referenz3>]])"
@@ -9782,15 +9782,15 @@ msgstr "gibt Tag-Inhalte aus"
 
 #: builtin/write-tree.c:13
 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
-msgstr "git write-tree [--missing-ok] [--prefix=<Prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<Präfix>/]"
 
 #: builtin/write-tree.c:26
 msgid "<prefix>/"
-msgstr "<Prefix>/"
+msgstr "<Präfix>/"
 
 #: builtin/write-tree.c:27
 msgid "write tree object for a subdirectory <prefix>"
-msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Prefix>"
+msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Präfix>"
 
 #: builtin/write-tree.c:30
 msgid "only useful for debugging"
index 962e82be8633217b811a8c8c87b892721b35f662..87db08bd749e74fef0b2372ab71ea98baf57b322 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -40,7 +40,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        const char *fmt;
        int i;
 
-       if (prefixcmp(var, "pretty."))
+       if (!starts_with(var, "pretty."))
                return 0;
 
        name = var + strlen("pretty.");
@@ -67,7 +67,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        commit_format->name = xstrdup(name);
        commit_format->format = CMIT_FMT_USERFORMAT;
        git_config_string(&fmt, var, value);
-       if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+       if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
                commit_format->is_tformat = fmt[0] == 't';
                fmt = strchr(fmt, ':') + 1;
        } else if (strchr(fmt, '%'))
@@ -115,7 +115,7 @@ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
        for (i = 0; i < commit_formats_len; i++) {
                size_t match_len;
 
-               if (prefixcmp(commit_formats[i].name, sought))
+               if (!starts_with(commit_formats[i].name, sought))
                        continue;
 
                match_len = strlen(commit_formats[i].name);
@@ -151,7 +151,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                rev->commit_format = CMIT_FMT_DEFAULT;
                return;
        }
-       if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
+       if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) {
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
@@ -840,10 +840,10 @@ static void parse_commit_header(struct format_commit_context *context)
 
                if (i == eol) {
                        break;
-               } else if (!prefixcmp(msg + i, "author ")) {
+               } else if (starts_with(msg + i, "author ")) {
                        context->author.off = i + 7;
                        context->author.len = eol - i - 7;
-               } else if (!prefixcmp(msg + i, "committer ")) {
+               } else if (starts_with(msg + i, "committer ")) {
                        context->committer.off = i + 10;
                        context->committer.len = eol - i - 10;
                }
@@ -983,7 +983,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 
                if (!end)
                        return 0;
-               if (!prefixcmp(begin, "auto,")) {
+               if (starts_with(begin, "auto,")) {
                        if (!want_color(c->pretty_ctx->color))
                                return end - placeholder + 1;
                        begin += 5;
@@ -994,16 +994,16 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
                strbuf_addstr(sb, color);
                return end - placeholder + 1;
        }
-       if (!prefixcmp(placeholder + 1, "red")) {
+       if (starts_with(placeholder + 1, "red")) {
                strbuf_addstr(sb, GIT_COLOR_RED);
                return 4;
-       } else if (!prefixcmp(placeholder + 1, "green")) {
+       } else if (starts_with(placeholder + 1, "green")) {
                strbuf_addstr(sb, GIT_COLOR_GREEN);
                return 6;
-       } else if (!prefixcmp(placeholder + 1, "blue")) {
+       } else if (starts_with(placeholder + 1, "blue")) {
                strbuf_addstr(sb, GIT_COLOR_BLUE);
                return 5;
-       } else if (!prefixcmp(placeholder + 1, "reset")) {
+       } else if (starts_with(placeholder + 1, "reset")) {
                strbuf_addstr(sb, GIT_COLOR_RESET);
                return 6;
        } else
@@ -1060,11 +1060,11 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
                        end = strchr(start, ')');
                        if (!end || end == start)
                                return 0;
-                       if (!prefixcmp(start, "trunc)"))
+                       if (starts_with(start, "trunc)"))
                                c->truncate = trunc_right;
-                       else if (!prefixcmp(start, "ltrunc)"))
+                       else if (starts_with(start, "ltrunc)"))
                                c->truncate = trunc_left;
-                       else if (!prefixcmp(start, "mtrunc)"))
+                       else if (starts_with(start, "mtrunc)"))
                                c->truncate = trunc_middle;
                        else
                                return 0;
@@ -1089,7 +1089,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
-               if (!prefixcmp(placeholder + 1, "(auto)")) {
+               if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = 1;
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
@@ -1556,7 +1556,7 @@ static void pp_header(struct pretty_print_context *pp,
                        continue;
                }
 
-               if (!prefixcmp(line, "parent ")) {
+               if (starts_with(line, "parent ")) {
                        if (linelen != 48)
                                die("bad parent line in commit");
                        continue;
@@ -1580,11 +1580,11 @@ static void pp_header(struct pretty_print_context *pp,
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
-               if (!prefixcmp(line, "author ")) {
+               if (starts_with(line, "author ")) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
-               if (!prefixcmp(line, "committer ") &&
+               if (starts_with(line, "committer ") &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Commit", sb, line + 10, encoding);
diff --git a/refs.c b/refs.c
index 5e5a3824b992132aaac8d86698feebcb773e13c6..39261368f5656273b0910c5b61a58d215afbcc5d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -637,7 +637,7 @@ static int do_one_ref(struct ref_entry *entry, void *cb_data)
        struct ref_entry *old_current_ref;
        int retval;
 
-       if (prefixcmp(entry->name, data->base))
+       if (!starts_with(entry->name, data->base))
                return 0;
 
        if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
@@ -1042,7 +1042,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                if (refname) {
                        last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
                        if (peeled == PEELED_FULLY ||
-                           (peeled == PEELED_TAGS && !prefixcmp(refname, "refs/tags/")))
+                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
                        add_ref(dir, last);
                        continue;
@@ -1376,7 +1376,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                                        return NULL;
                        }
                        buffer[len] = 0;
-                       if (!prefixcmp(buffer, "refs/") &&
+                       if (starts_with(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
                                strcpy(refname_buffer, buffer);
                                refname = refname_buffer;
@@ -1415,7 +1415,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                /*
                 * Is it a symbolic ref?
                 */
-               if (prefixcmp(buffer, "ref:")) {
+               if (!starts_with(buffer, "ref:")) {
                        /*
                         * Please note that FETCH_HEAD has a second
                         * line containing other data.
@@ -1837,7 +1837,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        struct ref_filter filter;
        int ret;
 
-       if (!prefix && prefixcmp(pattern, "refs/"))
+       if (!prefix && !starts_with(pattern, "refs/"))
                strbuf_addstr(&real_pattern, "refs/");
        else if (prefix)
                strbuf_addstr(&real_pattern, prefix);
@@ -1874,9 +1874,9 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
 const char *prettify_refname(const char *name)
 {
        return name + (
-               !prefixcmp(name, "refs/heads/") ? 11 :
-               !prefixcmp(name, "refs/tags/") ? 10 :
-               !prefixcmp(name, "refs/remotes/") ? 13 :
+               starts_with(name, "refs/heads/") ? 11 :
+               starts_with(name, "refs/tags/") ? 10 :
+               starts_with(name, "refs/remotes/") ? 13 :
                0);
 }
 
@@ -2244,7 +2244,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
        struct pack_refs_cb_data *cb = cb_data;
        enum peel_status peel_status;
        struct ref_entry *packed_entry;
-       int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
+       int is_tag_ref = starts_with(entry->name, "refs/tags/");
 
        /* ALWAYS pack tags */
        if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
@@ -2679,9 +2679,9 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
 
        git_snpath(logfile, bufsize, "logs/%s", refname);
        if (log_all_ref_updates &&
-           (!prefixcmp(refname, "refs/heads/") ||
-            !prefixcmp(refname, "refs/remotes/") ||
-            !prefixcmp(refname, "refs/notes/") ||
+           (starts_with(refname, "refs/heads/") ||
+            starts_with(refname, "refs/remotes/") ||
+            starts_with(refname, "refs/notes/") ||
             !strcmp(refname, "HEAD"))) {
                if (safe_create_leading_directories(logfile) < 0)
                        return error("unable to create directory for %s",
@@ -2751,7 +2751,7 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 
 static int is_branch(const char *refname)
 {
-       return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
 int write_ref_sha1(struct ref_lock *lock,
@@ -3450,7 +3450,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
 {
        if (!strcmp("transfer.hiderefs", var) ||
            /* NEEDSWORK: use parse_config_key() once both are merged */
-           (!prefixcmp(var, section) && var[strlen(section)] == '.' &&
+           (starts_with(var, section) && var[strlen(section)] == '.' &&
             !strcmp(var + strlen(section), ".hiderefs"))) {
                char *ref;
                int len;
@@ -3478,7 +3478,7 @@ int ref_is_hidden(const char *refname)
                return 0;
        for_each_string_list_item(item, hide_refs) {
                int len;
-               if (prefixcmp(refname, item->string))
+               if (!starts_with(refname, item->string))
                        continue;
                len = strlen(item->string);
                if (!refname[len] || refname[len] == '/')
index 91b07a41456fe5cdb2157a648353d982b88f7e9a..e38c4b026f2ad63181c765cd38ceb72ef30c4ee8 100644 (file)
@@ -217,7 +217,7 @@ static struct discovery* discover_refs(const char *service, int for_push)
        free_discovery(last);
 
        strbuf_addf(&refs_url, "%sinfo/refs", url.buf);
-       if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) &&
+       if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) &&
             git_env_bool("GIT_SMART_HTTP", 1)) {
                maybe_smart = 1;
                if (!strchr(url.buf, '?'))
@@ -766,7 +766,7 @@ static void parse_fetch(struct strbuf *buf)
        int alloc_heads = 0, nr_heads = 0;
 
        do {
-               if (!prefixcmp(buf->buf, "fetch ")) {
+               if (starts_with(buf->buf, "fetch ")) {
                        char *p = buf->buf + strlen("fetch ");
                        char *name;
                        struct ref *ref;
@@ -889,7 +889,7 @@ static void parse_push(struct strbuf *buf)
        int alloc_spec = 0, nr_spec = 0, i, ret;
 
        do {
-               if (!prefixcmp(buf->buf, "push ")) {
+               if (starts_with(buf->buf, "push ")) {
                        ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
                        specs[nr_spec++] = xstrdup(buf->buf + 5);
                }
@@ -952,19 +952,19 @@ int main(int argc, const char **argv)
                }
                if (buf.len == 0)
                        break;
-               if (!prefixcmp(buf.buf, "fetch ")) {
+               if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
                                die("Fetch attempted without a local repo");
                        parse_fetch(&buf);
 
-               } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+               } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
                        int for_push = !!strstr(buf.buf + 4, "for-push");
                        output_refs(get_refs(for_push));
 
-               } else if (!prefixcmp(buf.buf, "push ")) {
+               } else if (starts_with(buf.buf, "push ")) {
                        parse_push(&buf);
 
-               } else if (!prefixcmp(buf.buf, "option ")) {
+               } else if (starts_with(buf.buf, "option ")) {
                        char *name = buf.buf + strlen("option ");
                        char *value = strchr(name, ' ');
                        int result;
index d7cd5d272fd23624223f37ec8bda7d4fa8c39bce..078f1ffa417092e1c783b16d782b2aa69417a591 100644 (file)
@@ -82,7 +82,7 @@ static int parse_rev_note(const char *msg, struct rev_note *res)
                len = end ? end - msg : strlen(msg);
 
                key = "Revision-number: ";
-               if (!prefixcmp(msg, key)) {
+               if (starts_with(msg, key)) {
                        long i;
                        char *end;
                        value = msg + strlen(key);
@@ -154,7 +154,7 @@ static void check_or_regenerate_marks(int latestrev)
        } else {
                strbuf_addf(&sb, ":%d ", latestrev);
                while (strbuf_getline(&line, marksfile, '\n') != EOF) {
-                       if (!prefixcmp(line.buf, sb.buf)) {
+                       if (starts_with(line.buf, sb.buf)) {
                                found++;
                                break;
                        }
@@ -264,7 +264,7 @@ static int do_command(struct strbuf *line)
                return 1;       /* end of command stream, quit */
        }
        if (batch_cmd) {
-               if (prefixcmp(batch_cmd->name, line->buf))
+               if (!starts_with(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);
@@ -272,7 +272,7 @@ static int do_command(struct strbuf *line)
        }
 
        for (p = input_command_list; p->name; p++) {
-               if (!prefixcmp(line->buf, p->name) && (strlen(p->name) == line->len ||
+               if (starts_with(line->buf, p->name) && (strlen(p->name) == line->len ||
                                line->buf[strlen(p->name)] == ' ')) {
                        if (p->batchable) {
                                batch_cmd = p;
@@ -304,7 +304,7 @@ int main(int argc, char **argv)
        remote = remote_get(argv[1]);
        url_in = (argc == 3) ? argv[2] : remote->url[0];
 
-       if (!prefixcmp(url_in, "file://")) {
+       if (starts_with(url_in, "file://")) {
                dump_from_file = 1;
                url = url_decode(url_in + sizeof("file://")-1);
        } else {
index 15e6e5efcf1643b084b697a7daf563d901a15c02..a89efab5b766ba9374c3a09656b2d56593884c8e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -76,7 +76,7 @@ static const char *alias_url(const char *url, struct rewrites *r)
                if (!r->rewrite[i])
                        continue;
                for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
-                       if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
+                       if (starts_with(url, r->rewrite[i]->instead_of[j].s) &&
                            (!longest ||
                             longest->len < r->rewrite[i]->instead_of[j].len)) {
                                longest = &(r->rewrite[i]->instead_of[j]);
@@ -239,13 +239,13 @@ static void read_remotes_file(struct remote *remote)
                int value_list;
                char *s, *p;
 
-               if (!prefixcmp(buffer, "URL:")) {
+               if (starts_with(buffer, "URL:")) {
                        value_list = 0;
                        s = buffer + 4;
-               } else if (!prefixcmp(buffer, "Push:")) {
+               } else if (starts_with(buffer, "Push:")) {
                        value_list = 1;
                        s = buffer + 5;
-               } else if (!prefixcmp(buffer, "Pull:")) {
+               } else if (starts_with(buffer, "Pull:")) {
                        value_list = 2;
                        s = buffer + 5;
                } else
@@ -337,7 +337,7 @@ static int handle_config(const char *key, const char *value, void *cb)
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                name = key + 7;
                subkey = strrchr(name, '.');
                if (!subkey)
@@ -361,7 +361,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                }
                return 0;
        }
-       if (!prefixcmp(key, "url.")) {
+       if (starts_with(key, "url.")) {
                struct rewrite *rewrite;
                name = key + 4;
                subkey = strrchr(name, '.');
@@ -380,7 +380,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                }
        }
 
-       if (prefixcmp(key,  "remote."))
+       if (!starts_with(key,  "remote."))
                return 0;
        name = key + 7;
 
@@ -487,7 +487,7 @@ static void read_config(void)
        current_branch = NULL;
        head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
-           !prefixcmp(head_ref, "refs/heads/")) {
+           starts_with(head_ref, "refs/heads/")) {
                current_branch =
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
@@ -852,7 +852,7 @@ static int match_name_with_pattern(const char *key, const char *name,
        return ret;
 }
 
-static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
+int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
 {
        int i;
        int find_src = !query->src;
@@ -986,9 +986,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
        *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
 }
 
-static int count_refspec_match(const char *pattern,
-                              struct ref *refs,
-                              struct ref **matched_ref)
+int count_refspec_match(const char *pattern,
+                       struct ref *refs,
+                       struct ref **matched_ref)
 {
        int patlen = strlen(pattern);
        struct ref *matched_weak = NULL;
@@ -1013,8 +1013,8 @@ static int count_refspec_match(const char *pattern,
                 */
                if (namelen != patlen &&
                    patlen != namelen - 5 &&
-                   prefixcmp(name, "refs/heads/") &&
-                   prefixcmp(name, "refs/tags/")) {
+                   !starts_with(name, "refs/heads/") &&
+                   !starts_with(name, "refs/tags/")) {
                        /* We want to catch the case where only weak
                         * matches are found and there are multiple
                         * matches, and where more than one strong
@@ -1085,9 +1085,9 @@ static char *guess_ref(const char *name, struct ref *peer)
        if (!r)
                return NULL;
 
-       if (!prefixcmp(r, "refs/heads/"))
+       if (starts_with(r, "refs/heads/"))
                strbuf_addstr(&buf, "refs/heads/");
-       else if (!prefixcmp(r, "refs/tags/"))
+       else if (starts_with(r, "refs/tags/"))
                strbuf_addstr(&buf, "refs/tags/");
        else
                return NULL;
@@ -1135,7 +1135,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
-                    prefixcmp(dst_value, "refs/heads/")))
+                    !starts_with(dst_value, "refs/heads/")))
                        die("%s cannot be resolved to branch.",
                            matched_src->name);
        }
@@ -1224,7 +1224,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
                 * including refs outside refs/heads/ hierarchy, but
                 * that does not make much sense these days.
                 */
-               if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
+               if (!send_mirror && !starts_with(ref->name, "refs/heads/"))
                        return NULL;
                name = xstrdup(ref->name);
        }
@@ -1279,7 +1279,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
                        add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
                else
                        add_to_tips(&sent_tips, ref->old_sha1);
-               if (!prefixcmp(ref->name, "refs/tags/"))
+               if (starts_with(ref->name, "refs/tags/"))
                        string_list_append(&dst_tag, ref->name);
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
@@ -1288,7 +1288,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
 
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
@@ -1512,7 +1512,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 */
 
                else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
-                       if (!prefixcmp(ref->name, "refs/tags/"))
+                       if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_sha1_file(ref->old_sha1))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
@@ -1645,12 +1645,12 @@ static struct ref *get_local_ref(const char *name)
        if (!name || name[0] == '\0')
                return NULL;
 
-       if (!prefixcmp(name, "refs/"))
+       if (starts_with(name, "refs/"))
                return alloc_ref(name);
 
-       if (!prefixcmp(name, "heads/") ||
-           !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/"))
+       if (starts_with(name, "heads/") ||
+           starts_with(name, "tags/") ||
+           starts_with(name, "remotes/"))
                return alloc_ref_with_prefix("refs/", 5, name);
 
        return alloc_ref_with_prefix("refs/heads/", 11, name);
@@ -1685,7 +1685,7 @@ int get_fetch_map(const struct ref *remote_refs,
 
        for (rmp = &ref_map; *rmp; ) {
                if ((*rmp)->peer_ref) {
-                       if (prefixcmp((*rmp)->peer_ref->name, "refs/") ||
+                       if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
                            check_refname_format((*rmp)->peer_ref->name, 0)) {
                                struct ref *ignore = *rmp;
                                error("* Ignoring funny ref '%s' locally",
@@ -1969,7 +1969,7 @@ struct ref *guess_remote_head(const struct ref *head,
        /* Look for another ref that points there */
        for (r = refs; r; r = r->next) {
                if (r != head &&
-                   !prefixcmp(r->name, "refs/heads/") &&
+                   starts_with(r->name, "refs/heads/") &&
                    !hashcmp(r->old_sha1, head->old_sha1)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
index c07eb9950a15600f7fde7faa60570ea5a558fb60..00c6a76ef803005698e14d7f31eb4acd22b35649 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -128,6 +128,7 @@ struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
+extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
 int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -162,6 +163,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
 void free_refspec(int nr_refspec, struct refspec *refspec);
 
+extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);
 char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
                     const char *name);
 
index d0b1548726e9d2362d27c6947eb61a0647d0eac2..cdcaf8cbe2f4e6c7a5b334fc2c43da6855df4654 100644 (file)
@@ -97,9 +97,6 @@ const unsigned char *do_lookup_replace_object(const unsigned char *sha1)
        int pos, depth = MAXREPLACEDEPTH;
        const unsigned char *cur = sha1;
 
-       if (!read_replace_refs)
-               return sha1;
-
        prepare_replace_object();
 
        /* Try to recursively replace the object */
index 05d2d7763a3c9a2bd555cb19ef9af01c5767adb8..a68fde6e959c3f305fa7dba285783bd4300b8c0f 100644 (file)
@@ -1627,9 +1627,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
-           !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
-           !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
+           !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+           starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
+           starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
@@ -1652,7 +1652,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_count = atoi(argv[1]);
                revs->no_walk = 0;
                return 2;
-       } else if (!prefixcmp(arg, "-n")) {
+       } else if (starts_with(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
@@ -1712,7 +1712,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--author-date-order")) {
                revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
-       } else if (!prefixcmp(arg, "--early-output")) {
+       } else if (starts_with(arg, "--early-output")) {
                int count = 100;
                switch (arg[14]) {
                case '=':
@@ -1737,13 +1737,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
-       } else if (!prefixcmp(arg, "--min-parents=")) {
+       } else if (starts_with(arg, "--min-parents=")) {
                revs->min_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-min-parents")) {
+       } else if (starts_with(arg, "--no-min-parents")) {
                revs->min_parents = 0;
-       } else if (!prefixcmp(arg, "--max-parents=")) {
+       } else if (starts_with(arg, "--max-parents=")) {
                revs->max_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-max-parents")) {
+       } else if (starts_with(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
@@ -1793,7 +1793,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
-       } else if (!prefixcmp(arg, "--unpacked=")) {
+       } else if (starts_with(arg, "--unpacked=")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
@@ -1818,7 +1818,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
-       } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+       } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                 * not allowed, since the argument is optional.
@@ -1832,12 +1832,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
-       } else if (!prefixcmp(arg, "--show-notes=") ||
-                  !prefixcmp(arg, "--notes=")) {
+       } else if (starts_with(arg, "--show-notes=") ||
+                  starts_with(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
-               if (!prefixcmp(arg, "--show-notes")) {
+               if (starts_with(arg, "--show-notes")) {
                        if (revs->notes_opt.use_default_notes < 0)
                                revs->notes_opt.use_default_notes = 1;
                        strbuf_addstr(&buf, arg+13);
@@ -1880,7 +1880,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->abbrev = 0;
        } else if (!strcmp(arg, "--abbrev")) {
                revs->abbrev = DEFAULT_ABBREV;
-       } else if (!prefixcmp(arg, "--abbrev=")) {
+       } else if (starts_with(arg, "--abbrev=")) {
                revs->abbrev = strtoul(arg + 9, NULL, 10);
                if (revs->abbrev < MINIMUM_ABBREV)
                        revs->abbrev = MINIMUM_ABBREV;
@@ -2027,17 +2027,17 @@ static int handle_revision_pseudo_opt(const char *submodule,
        } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
                add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
-       } else if (!prefixcmp(arg, "--branches=")) {
+       } else if (starts_with(arg, "--branches=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (!prefixcmp(arg, "--tags=")) {
+       } else if (starts_with(arg, "--tags=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (!prefixcmp(arg, "--remotes=")) {
+       } else if (starts_with(arg, "--remotes=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
@@ -2048,7 +2048,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-       } else if (!prefixcmp(arg, "--no-walk=")) {
+       } else if (starts_with(arg, "--no-walk=")) {
                /*
                 * Detached form ("--no-walk X" as opposed to "--no-walk=X")
                 * not allowed, since the argument is optional.
index 9ee8cf50a830ed85f134825dcf1d41287a21f046..ac14a4d090cce0fa282447641097085a8428c05c 100644 (file)
@@ -109,7 +109,7 @@ static int receive_status(int in, struct ref *refs)
        struct ref *hint;
        int ret = 0;
        char *line = packet_read_line(in, NULL);
-       if (prefixcmp(line, "unpack "))
+       if (!starts_with(line, "unpack "))
                return error("did not receive remote status");
        if (strcmp(line, "unpack ok")) {
                error("unpack failed: %s", line + 7);
@@ -122,7 +122,7 @@ static int receive_status(int in, struct ref *refs)
                line = packet_read_line(in, NULL);
                if (!line)
                        break;
-               if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
+               if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
                        error("invalid ref status from remote: %s", line);
                        ret = -1;
                        break;
index 06e52b4c83ffc0ce2b9414afe3fb1b4ee91d4335..90cac7b02eef1e9f681f5dc58bc26becaaf5ac12 100644 (file)
@@ -41,7 +41,7 @@ static int is_cherry_picked_from_line(const char *buf, int len)
         * We only care that it looks roughly like (cherry picked from ...)
         */
        return len > strlen(cherry_picked_prefix) + 1 &&
-               !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
+               starts_with(buf, cherry_picked_prefix) && buf[len - 1] == ')';
 }
 
 /*
@@ -180,7 +180,7 @@ static char *get_encoding(const char *message)
        while (*p && *p != '\n') {
                for (eol = p + 1; *eol && *eol != '\n'; eol++)
                        ; /* do nothing */
-               if (!prefixcmp(p, "encoding ")) {
+               if (starts_with(p, "encoding ")) {
                        char *result = xmalloc(eol - 8 - p);
                        strlcpy(result, p + 9, eol - 8 - p);
                        return result;
@@ -705,10 +705,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *
        char *end_of_object_name;
        int saved, status, padding;
 
-       if (!prefixcmp(bol, "pick")) {
+       if (starts_with(bol, "pick")) {
                action = REPLAY_PICK;
                bol += strlen("pick");
-       } else if (!prefixcmp(bol, "revert")) {
+       } else if (starts_with(bol, "revert")) {
                action = REPLAY_REVERT;
                bol += strlen("revert");
        } else
diff --git a/setup.c b/setup.c
index 5432a31b62800bfba4d5a19226946ea30c5446a4..6c3f85ff7a7935ed6f05ab5ab376a2d6c5ca2d46 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -82,7 +82,7 @@ int check_filename(const char *prefix, const char *arg)
        const char *name;
        struct stat st;
 
-       if (!prefixcmp(arg, ":/")) {
+       if (starts_with(arg, ":/")) {
                if (arg[2] == '\0') /* ":/" is root dir, always exists */
                        return 1;
                name = arg + 2;
@@ -304,7 +304,7 @@ const char *read_gitfile(const char *path)
        if (len != st.st_size)
                die("Error reading %s", path);
        buf[len] = '\0';
-       if (prefixcmp(buf, "gitdir: "))
+       if (!starts_with(buf, "gitdir: "))
                die("Invalid gitfile format: %s", path);
        while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
                len--;
index daacc0cfb0132e98a98ba9645991f3d3f228d1cd..ee224e412889ce437e71afbc48f111ecae66e118 100644 (file)
@@ -2443,13 +2443,14 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        return 0;
 }
 
-int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
+int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
 {
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
+       const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 
-       co = find_cached_object(sha1);
+       co = find_cached_object(real);
        if (co) {
                if (oi->typep)
                        *(oi->typep) = co->type;
@@ -2461,23 +2462,23 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                return 0;
        }
 
-       if (!find_pack_entry(sha1, &e)) {
+       if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(sha1, oi)) {
+               if (!sha1_loose_object_info(real, oi)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
-               if (!find_pack_entry(sha1, &e))
+               if (!find_pack_entry(real, &e))
                        return -1;
        }
 
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
-               mark_bad_packed_object(e.p, sha1);
-               return sha1_object_info_extended(sha1, oi);
+               mark_bad_packed_object(e.p, real);
+               return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@ -2499,7 +2500,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 
        oi.typep = &type;
        oi.sizep = sizep;
-       if (sha1_object_info_extended(sha1, &oi) < 0)
+       if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
                return -1;
        return type;
 }
@@ -2591,8 +2592,7 @@ void *read_sha1_file_extended(const unsigned char *sha1,
        void *data;
        char *path;
        const struct packed_git *p;
-       const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
-               ? lookup_replace_object(sha1) : sha1;
+       const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
 
        errno = 0;
        data = read_object(repl, type, size);
index 2f37488b96c0a8485e90d87befa997d75ba4b7b1..b1873d8113bdeb0ff8087319c99833036aaffe1b 100644 (file)
@@ -546,7 +546,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
-                               if (!prefixcmp(real_ref, "refs/heads/")) {
+                               if (starts_with(real_ref, "refs/heads/")) {
                                        str = real_ref + 11;
                                        len = strlen(real_ref + 11);
                                } else {
@@ -674,15 +674,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                return -1;
 
        sp++; /* beginning of type name, or closing brace for empty */
-       if (!prefixcmp(sp, "commit}"))
+       if (starts_with(sp, "commit}"))
                expected_type = OBJ_COMMIT;
-       else if (!prefixcmp(sp, "tag}"))
+       else if (starts_with(sp, "tag}"))
                expected_type = OBJ_TAG;
-       else if (!prefixcmp(sp, "tree}"))
+       else if (starts_with(sp, "tree}"))
                expected_type = OBJ_TREE;
-       else if (!prefixcmp(sp, "blob}"))
+       else if (starts_with(sp, "blob}"))
                expected_type = OBJ_BLOB;
-       else if (!prefixcmp(sp, "object}"))
+       else if (starts_with(sp, "object}"))
                expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
@@ -909,7 +909,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
        const char *match = NULL, *target = NULL;
        size_t len;
 
-       if (!prefixcmp(message, "checkout: moving from ")) {
+       if (starts_with(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
                target = strstr(match, " to ");
        }
@@ -1302,7 +1302,7 @@ static void diagnose_invalid_index_path(int stage,
 
 static char *resolve_relative_path(const char *rel)
 {
-       if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
+       if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
 
        if (!startup_info)
diff --git a/shell.c b/shell.c
index 66350b220cc74abd383e10770f8325ba4e658e87..5c0d47a5cc1ae85a03e40321e97ddfa859ff7e1a 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -15,7 +15,7 @@ static int do_generic_cmd(const char *me, char *arg)
        setup_path();
        if (!arg || !(arg = sq_dequote(arg)))
                die("bad argument");
-       if (prefixcmp(me, "git-"))
+       if (!starts_with(me, "git-"))
                die("bad command");
 
        my_argv[0] = me + 4;
index 1170d01c4322b494cd900853279766edd030de32..83caf4a9147e6788107fb5fd23e6d9e1d6f6267f 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,6 +1,15 @@
 #include "cache.h"
 #include "refs.h"
 
+int starts_with(const char *str, const char *prefix)
+{
+       for (; ; str++, prefix++)
+               if (!*prefix)
+                       return 1;
+               else if (*str != *prefix)
+                       return 0;
+}
+
 int prefixcmp(const char *str, const char *prefix)
 {
        for (; ; str++, prefix++)
@@ -10,6 +19,15 @@ int prefixcmp(const char *str, const char *prefix)
                        return (unsigned char)*prefix - (unsigned char)*str;
 }
 
+int ends_with(const char *str, const char *suffix)
+{
+       int len = strlen(str), suflen = strlen(suffix);
+       if (len < suflen)
+               return 0;
+       else
+               return !strcmp(str + len - suflen, suffix);
+}
+
 int suffixcmp(const char *str, const char *suffix)
 {
        int len = strlen(str), suflen = strlen(suffix);
index debe904523252ae4fd2e18784641575c3f828dd2..9659f18be263a3412a2f3e5d13eb4f4bcd25cee3 100644 (file)
@@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1,
 
        oi->typep = type;
        oi->sizep = &size;
-       status = sha1_object_info_extended(sha1, oi);
+       status = sha1_object_info_extended(sha1, oi, 0);
        if (status < 0)
                return stream_error;
 
index 1905d75b2b09ee7e1503b35bf072710a659ea1ef..613857e400f826f3d4ea28c87025b2c2df42408a 100644 (file)
@@ -201,7 +201,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 
 int submodule_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
                config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh
new file mode 100755 (executable)
index 0000000..683be69
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description="Test diff --no-index performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+file1=$(git ls-files | tail -n 2 | head -1)
+file2=$(git ls-files | tail -n 1 | head -1)
+
+test_expect_success "empty files, so they take no time to diff" "
+       echo >$file1 &&
+       echo >$file2
+"
+
+test_perf "diff --no-index" "
+       git diff --no-index $file1 $file2 >/dev/null
+"
+
+test_done
index cb144258ccc3f32025c4b7beba6ac02c427612d8..37e9396e5dae8957ba73507018d5c334ee65e30d 100755 (executable)
@@ -7,7 +7,7 @@ Verify that plumbing commands work when .git is a file
 . ./test-lib.sh
 
 objpath() {
-    echo "$1" | sed -e 's|\(..\)|\1/|'
+       echo "$1" | sed -e 's|\(..\)|\1/|'
 }
 
 objck() {
@@ -19,7 +19,6 @@ objck() {
        fi
 }
 
-
 test_expect_success 'initial setup' '
        REAL="$(pwd)/.real" &&
        mv .git "$REAL"
index 0b98b6f8d090ecc3dfb48fe6fda5889dfbc5e018..b9d79476e20b4ddf0f9e85c672df2061fb910f9d 100755 (executable)
@@ -13,7 +13,6 @@ attr_check () {
        test_line_count = 0 err
 }
 
-
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
index 8a1bc5c53281eb6f44c15d68dd0ae737bc0125f3..1687098355b710850a04769512320930d9b8b27b 100755 (executable)
@@ -85,6 +85,28 @@ $content"
                git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
        test_cmp expect actual
     '
+
+    test -z "$content" ||
+    test_expect_success "--batch without type ($type)" '
+       {
+               echo "$size" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
+
+    test -z "$content" ||
+    test_expect_success "--batch without size ($type)" '
+       {
+               echo "$type" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
 }
 
 hello_content="Hello World"
index f950c101287c2f05bbd70c1faffbcf2cda1edbee..613d9bfe1bbb153d1c2e647aee26a60e07a6af86 100755 (executable)
@@ -196,4 +196,28 @@ test_expect_success 'dotdot is not an empty set' '
        test_cmp expect actual
 '
 
+test_expect_success 'arg before dashdash must be a revision (missing)' '
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (file)' '
+       >foobar &&
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
+       >foobar &&
+       git update-ref refs/heads/foobar HEAD &&
+       {
+               # we do not want to use rev-parse here, because
+               # we are testing it
+               cat .git/refs/heads/foobar &&
+               printf "%s\n" --
+       } >expect &&
+       git rev-parse foobar -- >actual &&
+       test_cmp expect actual
+'
+
 test_done
index ebf93b0695dfac37fc276e1f07377d858eb3e1da..998503db12eb115e8e160937d456dfd9cb46311f 100755 (executable)
@@ -134,12 +134,14 @@ test_expect_success 'fail when upstream arg is missing and not configured' '
        test_must_fail git rebase
 '
 
-test_expect_success 'default to @{upstream} when upstream arg is missing' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
        git checkout -b default topic &&
        git config branch.default.remote . &&
        git config branch.default.merge refs/heads/master &&
        git rebase &&
-       test "$(git rev-parse default~1)" = "$(git rev-parse master)"
+       git rev-parse --verify master >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -q is quiet' '
index 8a309795c9c9654446d82c2e474e1ca08486e081..bcae35ac1c93aaf79032db5ee09fd2d3400101f5 100755 (executable)
@@ -193,6 +193,19 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
        GIT_EXTERNAL_DIFF=echo git diff
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
+       write_script external-diff.sh <<-\EOF &&
+       echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
+       EOF
+       >counter.txt &&
+       cat >expect <<-\EOF &&
+       1 of 2
+       2 of 2
+       EOF
+       GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
+       test_cmp expect counter.txt
+'
+
 test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
        touch file.ext &&
        git add file.ext &&
index 979e98398bebc21fe664ca4a19770e5b251bfc97..2ab3c487340569d7f2f8164776907ef30df2d415 100755 (executable)
@@ -29,4 +29,30 @@ test_expect_success 'git diff --no-index relative path outside repo' '
        )
 '
 
+test_expect_success 'git diff --no-index with broken index' '
+       (
+               cd repo &&
+               echo broken >.git/index &&
+               git diff --no-index a ../non/git/a
+       )
+'
+
+test_expect_success 'git diff outside repo with broken index' '
+       (
+               cd repo &&
+               git diff ../non/git/a ../non/git/b
+       )
+'
+
+test_expect_success 'git diff --no-index executed outside repo gives correct error message' '
+       (
+               GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_must_fail git diff --no-index a 2>actual.err &&
+               echo "usage: git diff --no-index <path> <path>" >expect.err &&
+               test_cmp expect.err actual.err
+       )
+'
+
 test_done
index 11bf0b8bda9998d1f533d2d28c5b7d468657ae24..05f011d38eeaf16e2be8caace5bca697b7a9daf3 100755 (executable)
@@ -25,8 +25,6 @@ commit id embedding:
 '
 
 . ./test-lib.sh
-GZIP=${GZIP:-gzip}
-GUNZIP=${GUNZIP:-gzip -d}
 
 SUBSTFORMAT=%H%n
 
@@ -39,6 +37,8 @@ test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
        )
 '
 
+test_lazy_prereq GZIP 'gzip --version'
+
 get_pax_header() {
        file=$1
        header=$2=
@@ -265,12 +265,6 @@ test_expect_success 'only enabled filters are available remotely' '
        test_cmp remote.bar config.bar
 '
 
-if $GZIP --version >/dev/null 2>&1; then
-       test_set_prereq GZIP
-else
-       say "Skipping some tar.gz tests because gzip not found"
-fi
-
 test_expect_success GZIP 'git archive --format=tgz' '
        git archive --format=tgz HEAD >j.tgz
 '
@@ -290,14 +284,8 @@ test_expect_success GZIP 'infer tgz from .tar.gz filename' '
        test_cmp j.tgz j3.tar.gz
 '
 
-if $GUNZIP --version >/dev/null 2>&1; then
-       test_set_prereq GUNZIP
-else
-       say "Skipping some tar.gz tests because gunzip was not found"
-fi
-
-test_expect_success GZIP,GUNZIP 'extract tgz file' '
-       $GUNZIP -c <j.tgz >j.tar &&
+test_expect_success GZIP 'extract tgz file' '
+       gzip -d -c <j.tgz >j.tar &&
        test_cmp b.tar j.tar
 '
 
index fe82025d4a0dbae0cee3d0c3943617ede5b5c7a5..4bbb718751738c3e913c25160d0fff52d8350bba 100755 (executable)
@@ -174,11 +174,11 @@ test_expect_success \
 test_expect_success \
     '[index v1] 5) pack-objects happily reuses corrupted data' \
     'pack4=$(git pack-objects test-4 <obj-list) &&
-     test -f "test-4-${pack1}.pack"'
+     test -f "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v1] 6) newly created pack is BAD !' \
-    'test_must_fail git verify-pack -v "test-4-${pack1}.pack"'
+    'test_must_fail git verify-pack -v "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
index d87ddf73b7127bc624ca739653db9389831b817e..5b2b1c2c130387332778f6e49043b739dd2d7d21 100755 (executable)
@@ -531,5 +531,92 @@ test_expect_success 'shallow fetch with tags does not break the repository' '
                git fsck
        )
 '
+check_prot_path () {
+       cat >expected <<-EOF &&
+       Diag: url=$1
+       Diag: protocol=$2
+       Diag: path=$3
+       EOF
+       git fetch-pack --diag-url "$1" | grep -v hostandport= >actual &&
+       test_cmp expected actual
+}
+
+check_prot_host_path () {
+       cat >expected <<-EOF &&
+       Diag: url=$1
+       Diag: protocol=$2
+       Diag: hostandport=$3
+       Diag: path=$4
+       EOF
+       git fetch-pack --diag-url "$1" >actual &&
+       test_cmp expected actual
+}
+
+for r in repo re:po re/po
+do
+       # git or ssh with scheme
+       for p in "ssh+git" "git+ssh" git ssh
+       do
+               for h in host host:12 [::1] [::1]:23
+               do
+                       case "$p" in
+                       *ssh*)
+                               pp=ssh
+                               ;;
+                       *)
+                               pp=$p
+                       ;;
+                       esac
+                       test_expect_success "fetch-pack --diag-url $p://$h/$r" '
+                               check_prot_host_path $p://$h/$r $pp "$h" "/$r"
+                       '
+                       # "/~" -> "~" conversion
+                       test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
+                               check_prot_host_path $p://$h/~$r $pp "$h" "~$r"
+                       '
+               done
+       done
+       # file with scheme
+       for p in file
+       do
+               test_expect_success "fetch-pack --diag-url $p://$h/$r" '
+                       check_prot_path $p://$h/$r $p "/$r"
+               '
+               # No "/~" -> "~" conversion for file
+               test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
+                       check_prot_path $p://$h/~$r $p "/~$r"
+               '
+       done
+       # file without scheme
+       for h in nohost nohost:12 [::1] [::1]:23 [ [:aa
+       do
+               test_expect_success "fetch-pack --diag-url ./$h:$r" '
+                       check_prot_path ./$h:$r $p "./$h:$r"
+               '
+               # No "/~" -> "~" conversion for file
+               test_expect_success "fetch-pack --diag-url ./$p:$h/~$r" '
+               check_prot_path ./$p:$h/~$r $p "./$p:$h/~$r"
+               '
+       done
+       #ssh without scheme
+       p=ssh
+       for h in host [::1]
+       do
+               test_expect_success "fetch-pack --diag-url $h:$r" '
+                       check_prot_path $h:$r $p "$r"
+               '
+               # Do "/~" -> "~" conversion
+               test_expect_success "fetch-pack --diag-url $h:/~$r" '
+                       check_prot_host_path $h:/~$r $p "$h" "~$r"
+               '
+       done
+done
+
+test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' '
+       check_prot_path file://c:/repo file c:/repo
+'
+test_expect_success MINGW 'fetch-pack --diag-url c:repo' '
+       check_prot_path c:repo file c:repo
+'
 
 test_done
index 99c32d75391bb02fae779ad831879903abca5d25..926e7f6b979d4d1c69ecc9d280cd6de365627012 100755 (executable)
@@ -1126,6 +1126,81 @@ test_expect_success 'fetch follows tags by default' '
        test_cmp expect actual
 '
 
+test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" &&
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               test_must_fail git show-ref refs/heads/master &&
+               git show-ref refs/remotes/src/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, it is not used as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config push.default matching &&
+               git push dst master &&
+               git show-ref refs/heads/master >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, upstream mapping is used' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" &&
+               git config push.default upstream &&
+
+               git config branch.master.merge refs/heads/trunk &&
+               git config branch.master.remote dst &&
+
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/master &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/trunk >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
 test_expect_success 'push does not follow tags by default' '
        mk_test testrepo heads/master &&
        rm -fr src dst &&
index 1d1c8755ead4e432744a82f0d7b8ed7dfc8a6510..62fbd7e6649d9215528a83249f1b46735de2237d 100755 (executable)
@@ -280,25 +280,26 @@ test_expect_success 'clone checking out a tag' '
        test_cmp fetch.expected fetch.actual
 '
 
-test_expect_success 'setup ssh wrapper' '
-       write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF &&
-       echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" &&
-       # throw away all but the last argument, which should be the
-       # command
-       while test $# -gt 1; do shift; done
-       eval "$1"
-       EOF
-
-       GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
-       export GIT_SSH &&
-       export TRASH_DIRECTORY
-'
-
-clear_ssh () {
-       >"$TRASH_DIRECTORY/ssh-output"
+setup_ssh_wrapper () {
+       test_expect_success 'setup ssh wrapper' '
+               write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF &&
+               echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" &&
+               # throw away all but the last argument, which should be the
+               # command
+               while test $# -gt 1; do shift; done
+               eval "$1"
+               EOF
+               GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
+               export GIT_SSH &&
+               export TRASH_DIRECTORY &&
+               >"$TRASH_DIRECTORY"/ssh-output
+       '
 }
 
 expect_ssh () {
+       test_when_finished '
+               (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)
+       ' &&
        {
                case "$1" in
                none)
@@ -310,25 +311,114 @@ expect_ssh () {
        (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
 }
 
-test_expect_success 'cloning myhost:src uses ssh' '
-       clear_ssh &&
+setup_ssh_wrapper
+
+test_expect_success 'clone myhost:src uses ssh' '
        git clone myhost:src ssh-clone &&
        expect_ssh myhost src
 '
 
 test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
-       clear_ssh &&
        cp -R src "foo:bar" &&
-       git clone "./foo:bar" foobar &&
+       git clone "foo:bar" foobar &&
        expect_ssh none
 '
 
 test_expect_success 'bracketed hostnames are still ssh' '
-       clear_ssh &&
        git clone "[myhost:123]:src" ssh-bracket-clone &&
        expect_ssh myhost:123 src
 '
 
+counter=0
+# $1 url
+# $2 none|host
+# $3 path
+test_clone_url () {
+       counter=$(($counter + 1))
+       test_might_fail git clone "$1" tmp$counter &&
+       expect_ssh "$2" "$3"
+}
+
+test_expect_success NOT_MINGW 'clone c:temp is ssl' '
+       test_clone_url c:temp c temp
+'
+
+test_expect_success MINGW 'clone c:temp is dos drive' '
+       test_clone_url c:temp none
+'
+
+#ip v4
+for repo in rep rep/home/project 123
+do
+       test_expect_success "clone host:$repo" '
+               test_clone_url host:$repo host $repo
+       '
+done
+
+#ipv6
+for repo in rep rep/home/project 123
+do
+       test_expect_success "clone [::1]:$repo" '
+               test_clone_url [::1]:$repo ::1 $repo
+       '
+done
+#home directory
+test_expect_success "clone host:/~repo" '
+       test_clone_url host:/~repo host "~repo"
+'
+
+test_expect_success "clone [::1]:/~repo" '
+       test_clone_url [::1]:/~repo ::1 "~repo"
+'
+
+# Corner cases
+for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz
+do
+       test_expect_success "clone $url is not ssh" '
+               test_clone_url $url none
+       '
+done
+
+#with ssh:// scheme
+test_expect_success 'clone ssh://host.xz/home/user/repo' '
+       test_clone_url "ssh://host.xz/home/user/repo" host.xz "/home/user/repo"
+'
+
+# from home directory
+test_expect_success 'clone ssh://host.xz/~repo' '
+       test_clone_url "ssh://host.xz/~repo" host.xz "~repo"
+'
+
+# with port number
+test_expect_success 'clone ssh://host.xz:22/home/user/repo' '
+       test_clone_url "ssh://host.xz:22/home/user/repo" "-p 22 host.xz" "/home/user/repo"
+'
+
+# from home directory with port number
+test_expect_success 'clone ssh://host.xz:22/~repo' '
+       test_clone_url "ssh://host.xz:22/~repo" "-p 22 host.xz" "~repo"
+'
+
+#IPv6
+test_expect_success 'clone ssh://[::1]/home/user/repo' '
+       test_clone_url "ssh://[::1]/home/user/repo" "::1" "/home/user/repo"
+'
+
+#IPv6 from home directory
+test_expect_success 'clone ssh://[::1]/~repo' '
+       test_clone_url "ssh://[::1]/~repo" "::1" "~repo"
+'
+
+#IPv6 with port number
+test_expect_success 'clone ssh://[::1]:22/home/user/repo' '
+       test_clone_url "ssh://[::1]:22/home/user/repo" "-p 22 ::1" "/home/user/repo"
+'
+
+#IPv6 from home directory with port number
+test_expect_success 'clone ssh://[::1]:22/~repo' '
+       test_clone_url "ssh://[::1]:22/~repo" "-p 22 ::1" "~repo"
+'
+
 test_expect_success 'clone from a repository with two identical branches' '
 
        (
index 7d479843522e3b183fdb4d72d86909b53e0edbda..719a11673bfc93463870d7d601eb3a0634b59b48 100755 (executable)
@@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' '
        git replace -f HEAD^ $BLOB
 '
 
+test_expect_success 'git cat-file --batch works on replace objects' '
+       git replace | grep $PARA3 &&
+       echo $PARA3 | git cat-file --batch
+'
+
+test_expect_success 'test --format bogus' '
+       test_must_fail git replace --format bogus >/dev/null 2>&1
+'
+
+test_expect_success 'test --format short' '
+       git replace --format=short >actual &&
+       git replace >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format medium' '
+       H1=$(git --no-replace-objects rev-parse HEAD~1) &&
+       HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
+       MYTAG=$(git --no-replace-objects rev-parse mytag) &&
+       {
+               echo "$H1 -> $BLOB" &&
+               echo "$BLOB -> $REPLACED" &&
+               echo "$HT -> $H1" &&
+               echo "$PARA3 -> $S" &&
+               echo "$MYTAG -> $HASH1"
+       } | sort >expected &&
+       git replace -l --format medium | sort > actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format long' '
+       {
+               echo "$H1 (commit) -> $BLOB (blob)" &&
+               echo "$BLOB (blob) -> $REPLACED (blob)" &&
+               echo "$HT (tree) -> $H1 (commit)" &&
+               echo "$PARA3 (commit) -> $S (commit)" &&
+               echo "$MYTAG (tag) -> $HASH1 (commit)"
+       } | sort >expected &&
+       git replace --format=long | sort > actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'replace ref cleanup' '
        test -n "$(git replace)" &&
        git replace -d $(git replace) &&
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
new file mode 100755 (executable)
index 0000000..62049be
--- /dev/null
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description='test case exclude pathspec'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       for p in file sub/file sub/sub/file sub/file2 sub/sub/sub/file sub2/file; do
+               if echo $p | grep /; then
+                       mkdir -p `dirname $p`
+               fi &&
+               : >$p &&
+               git add $p &&
+               git commit -m $p
+       done &&
+       git log --oneline --format=%s >actual &&
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'exclude only should error out' '
+       test_must_fail git log --oneline --format=%s -- ":(exclude)sub"
+'
+
+test_expect_success 't_e_i() exclude sub' '
+       git log --oneline --format=%s -- . ":(exclude)sub" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub/sub/file' '
+       git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub using mnemonic' '
+       git log --oneline --format=%s -- . ":!sub" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(icase)SUB' '
+       git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub2 from sub' '
+       (
+       cd sub &&
+       git log --oneline --format=%s -- :/ ":/!sub2" >actual
+       cat <<EOF >expect &&
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 't_e_i() exclude sub/*file' '
+       git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/file2
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(glob)sub/*/file' '
+       git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub' '
+       git ls-files -- . ":(exclude)sub" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub/sub/file' '
+       git ls-files -- . ":(exclude)sub/sub/file" >actual
+       cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub using mnemonic' '
+       git ls-files -- . ":!sub" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(icase)SUB' '
+       git ls-files -- . ":(exclude,icase)SUB" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub2 from sub' '
+       (
+       cd sub &&
+       git ls-files -- :/ ":/!sub2" >actual
+       cat <<EOF >expect &&
+../file
+file
+file2
+sub/file
+sub/sub/file
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'm_p_d() exclude sub/*file' '
+       git ls-files -- . ":(exclude)sub/*file" >actual
+       cat <<EOF >expect &&
+file
+sub/file2
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(glob)sub/*/file' '
+       git ls-files -- . ":(exclude,glob)sub/*/file" >actual
+       cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_done
index b90e985a48efbf8cdb16c6166356f6984ffbcea8..3bfdfed1f7774edb8a87864bdb6a021dc6a185ed 100755 (executable)
@@ -70,6 +70,35 @@ test_expect_success \
 rm -f idontexist untracked1 untracked2 \
      path0/idontexist path0/untracked1 path0/untracked2 \
      .git/index.lock
+rmdir path1
+
+test_expect_success \
+    'moving to absent target with trailing slash' \
+    'test_must_fail git mv path0/COPYING no-such-dir/ &&
+     test_must_fail git mv path0/COPYING no-such-dir// &&
+     git mv path0/ no-such-dir/ &&
+     test_path_is_dir no-such-dir'
+
+test_expect_success \
+    'clean up' \
+    'git reset --hard'
+
+test_expect_success \
+    'moving to existing untracked target with trailing slash' \
+    'mkdir path1 &&
+     git mv path0/ path1/ &&
+     test_path_is_dir path1/path0/'
+
+test_expect_success \
+    'moving to existing tracked target with trailing slash' \
+    'mkdir path2 &&
+     >path2/file && git add path2/file &&
+     git mv path1/path0/ path2/ &&
+     test_path_is_dir path2/path0/'
+
+test_expect_success \
+    'clean up' \
+    'git reset --hard'
 
 test_expect_success \
     'adding another file' \
index da5bd3b5a585667dad97481df27049f0aa7415eb..2ddf28c984de99cb17884f76d68a708076f57e12 100755 (executable)
@@ -65,9 +65,35 @@ test_expect_success 'diff in message is retained without -v' '
        check_message diff
 '
 
-test_expect_failure 'diff in message is retained with -v' '
+test_expect_success 'diff in message is retained with -v' '
        git commit --amend -F diff -v &&
        check_message diff
 '
 
+test_expect_success 'submodule log is stripped out too with -v' '
+       git config diff.submodule log &&
+       git submodule add ./. sub &&
+       git commit -m "sub added" &&
+       (
+               cd sub &&
+               echo "more" >>file &&
+               git commit -a -m "submodule commit"
+       ) &&
+       (
+               GIT_EDITOR=cat &&
+               export GIT_EDITOR &&
+               test_must_fail git commit -a -v 2>err
+       ) &&
+       test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'verbose diff is stripped out with set core.commentChar' '
+       (
+               GIT_EDITOR=cat &&
+               export GIT_EDITOR &&
+               test_must_fail git -c core.commentchar=";" commit -a -v 2>err
+       ) &&
+       test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
 test_done
diff --git a/tag.c b/tag.c
index 78d272b863f22285048cf54b9dcb03f80cb36f00..7b07921b30641994ab3a877b6b377c5ce1f40149 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -86,7 +86,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
                return -1;
        bufptr += 48; /* "object " + sha1 + "\n" */
 
-       if (prefixcmp(bufptr, "type "))
+       if (!starts_with(bufptr, "type "))
                return -1;
        bufptr += 5;
        nl = memchr(bufptr, '\n', tail - bufptr);
@@ -109,7 +109,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
                item->tagged = NULL;
        }
 
-       if (bufptr + 4 < tail && !prefixcmp(bufptr, "tag "))
+       if (bufptr + 4 < tail && starts_with(bufptr, "tag "))
                ;               /* good */
        else
                return -1;
@@ -120,7 +120,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
        item->tag = xmemdupz(bufptr, nl - bufptr);
        bufptr = nl + 1;
 
-       if (bufptr + 7 < tail && !prefixcmp(bufptr, "tagger "))
+       if (bufptr + 7 < tail && starts_with(bufptr, "tagger "))
                item->date = parse_tag_date(bufptr, tail);
        else
                item->date = 0;
@@ -160,8 +160,8 @@ size_t parse_signature(const char *buf, unsigned long size)
 {
        char *eol;
        size_t len = 0;
-       while (len < size && prefixcmp(buf + len, PGP_SIGNATURE) &&
-                       prefixcmp(buf + len, PGP_MESSAGE)) {
+       while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
+                       !starts_with(buf + len, PGP_MESSAGE)) {
                eol = memchr(buf + len, '\n', size - len);
                len += eol ? eol - (buf + len) + 1 : size - len;
        }
index ef1d7bae1441e07b6cb74e83a23d07b4720a8ebf..1e58f0476f3465f9b8b361cc4776abf5c051430b 100644 (file)
@@ -19,7 +19,7 @@ static void handle_command(const char *command, const char *arg, struct line_buf
 {
        switch (*command) {
        case 'b':
-               if (!prefixcmp(command, "binary ")) {
+               if (starts_with(command, "binary ")) {
                        struct strbuf sb = STRBUF_INIT;
                        strbuf_addch(&sb, '>');
                        buffer_read_binary(buf, &sb, strtouint32(arg));
@@ -28,12 +28,12 @@ static void handle_command(const char *command, const char *arg, struct line_buf
                        return;
                }
        case 'c':
-               if (!prefixcmp(command, "copy ")) {
+               if (starts_with(command, "copy ")) {
                        buffer_copy_bytes(buf, strtouint32(arg));
                        return;
                }
        case 's':
-               if (!prefixcmp(command, "skip ")) {
+               if (starts_with(command, "skip ")) {
                        buffer_skip_bytes(buf, strtouint32(arg));
                        return;
                }
index 00ce6c9a129674c1922e0c388366592c5ae7c8c7..14bdf9d2153a98d0b2a5d03395c1adcc166f8a07 100644 (file)
@@ -38,7 +38,7 @@ static void write_list_compact(const struct string_list *list)
 static int prefix_cb(struct string_list_item *item, void *cb_data)
 {
        const char *prefix = (const char *)cb_data;
-       return !prefixcmp(item->string, prefix);
+       return starts_with(item->string, prefix);
 }
 
 int main(int argc, char **argv)
index 673b7c214f981cf158a8790b7278e37611c700fa..2010674bb4add9c09d02d703b5337a69b14a58a6 100644 (file)
@@ -190,7 +190,7 @@ static struct child_process *get_helper(struct transport *transport)
                        data->export = 1;
                else if (!strcmp(capname, "check-connectivity"))
                        data->check_connectivity = 1;
-               else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
+               else if (!data->refspecs && starts_with(capname, "refspec ")) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
@@ -199,17 +199,17 @@ static struct child_process *get_helper(struct transport *transport)
                        data->connect = 1;
                } else if (!strcmp(capname, "signed-tags")) {
                        data->signed_tags = 1;
-               } else if (!prefixcmp(capname, "export-marks ")) {
+               } else if (starts_with(capname, "export-marks ")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--export-marks=");
                        strbuf_addstr(&arg, capname + strlen("export-marks "));
                        data->export_marks = strbuf_detach(&arg, NULL);
-               } else if (!prefixcmp(capname, "import-marks")) {
+               } else if (starts_with(capname, "import-marks")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--import-marks=");
                        strbuf_addstr(&arg, capname + strlen("import-marks "));
                        data->import_marks = strbuf_detach(&arg, NULL);
-               } else if (!prefixcmp(capname, "no-private-update")) {
+               } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
@@ -311,7 +311,7 @@ static int set_helper_option(struct transport *transport,
 
        if (!strcmp(buf.buf, "ok"))
                ret = 0;
-       else if (!prefixcmp(buf.buf, "error")) {
+       else if (starts_with(buf.buf, "error")) {
                ret = -1;
        } else if (!strcmp(buf.buf, "unsupported"))
                ret = 1;
@@ -375,7 +375,7 @@ static int fetch_with_fetch(struct transport *transport,
        while (1) {
                recvline(data, &buf);
 
-               if (!prefixcmp(buf.buf, "lock ")) {
+               if (starts_with(buf.buf, "lock ")) {
                        const char *name = buf.buf + 5;
                        if (transport->pack_lockfile)
                                warning("%s also locked %s", data->name, name);
@@ -646,10 +646,10 @@ static int push_update_ref_status(struct strbuf *buf,
        char *refname, *msg;
        int status;
 
-       if (!prefixcmp(buf->buf, "ok ")) {
+       if (starts_with(buf->buf, "ok ")) {
                status = REF_STATUS_OK;
                refname = buf->buf + 3;
-       } else if (!prefixcmp(buf->buf, "error ")) {
+       } else if (starts_with(buf->buf, "error ")) {
                status = REF_STATUS_REMOTE_REJECT;
                refname = buf->buf + 6;
        } else
index 7202b7777d804b75eb2ec54f666e81173690678c..824c5b93f9e1c44887b17ef116b360ec377076a6 100644 (file)
@@ -169,13 +169,13 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                remotename = ref->name;
                tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
                if (tmp && flag & REF_ISSYMREF &&
-                       !prefixcmp(tmp, "refs/heads/"))
+                       starts_with(tmp, "refs/heads/"))
                        localname = tmp;
 
                /* Both source and destination must be local branches. */
-               if (!localname || prefixcmp(localname, "refs/heads/"))
+               if (!localname || !starts_with(localname, "refs/heads/"))
                        continue;
-               if (!remotename || prefixcmp(remotename, "refs/heads/"))
+               if (!remotename || !starts_with(remotename, "refs/heads/"))
                        continue;
 
                if (!pretend)
@@ -191,7 +191,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 
 static const char *rsync_url(const char *url)
 {
-       return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+       return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
 }
 
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
@@ -296,8 +296,8 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
        FILE *f;
 
        /* when called via for_each_ref(), flags is non-zero */
-       if (flags && prefixcmp(name, "refs/heads/") &&
-                       prefixcmp(name, "refs/tags/"))
+       if (flags && !starts_with(name, "refs/heads/") &&
+                       !starts_with(name, "refs/tags/"))
                return 0;
 
        strbuf_addstr(buf, name);
@@ -652,7 +652,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
                print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
        else if (is_null_sha1(ref->old_sha1))
                print_ref_status('*',
-                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+                       (starts_with(ref->name, "refs/tags/") ? "[new tag]" :
                        "[new branch]"),
                        ref, ref->peer_ref, NULL, porcelain);
        else {
@@ -885,14 +885,6 @@ void transport_take_over(struct transport *transport,
        transport->cannot_reuse = 1;
 }
 
-static int is_local(const char *url)
-{
-       const char *colon = strchr(url, ':');
-       const char *slash = strchr(url, '/');
-       return !colon || (slash && slash < colon) ||
-               has_dos_drive_prefix(url);
-}
-
 static int is_file(const char *url)
 {
        struct stat buf;
@@ -930,18 +922,18 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
                while (is_urlschemechar(p == url, *p))
                        p++;
-               if (!prefixcmp(p, "::"))
+               if (starts_with(p, "::"))
                        helper = xstrndup(url, p - url);
        }
 
        if (helper) {
                transport_helper_init(ret, helper);
-       } else if (!prefixcmp(url, "rsync:")) {
+       } else if (starts_with(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
                ret->smart_options = NULL;
-       } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
+       } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
@@ -949,11 +941,11 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->disconnect = close_bundle;
                ret->smart_options = NULL;
        } else if (!is_url(url)
-               || !prefixcmp(url, "file://")
-               || !prefixcmp(url, "git://")
-               || !prefixcmp(url, "ssh://")
-               || !prefixcmp(url, "git+ssh://")
-               || !prefixcmp(url, "ssh+git://")) {
+               || starts_with(url, "file://")
+               || starts_with(url, "git://")
+               || starts_with(url, "ssh://")
+               || starts_with(url, "git+ssh://")
+               || starts_with(url, "ssh+git://")) {
                /* These are builtin smart transports. */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
@@ -1297,7 +1289,7 @@ char *transport_anonymize_url(const char *url)
        size_t anon_len, prefix_len = 0;
 
        anon_part = strchr(url, '@');
-       if (is_local(url) || !anon_part)
+       if (url_is_local_not_ssh(url) || !anon_part)
                goto literal_copy;
 
        anon_len = strlen(++anon_part);
index 5ece8c3477b11f3bf0c54196924ac24568309225..680afda060e28fd15c0064bd51cb829dc2469b1c 100644 (file)
@@ -662,9 +662,10 @@ static int match_wildcard_base(const struct pathspec_item *item,
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-enum interesting tree_entry_interesting(const struct name_entry *entry,
-                                       struct strbuf *base, int base_offset,
-                                       const struct pathspec *ps)
+static enum interesting do_match(const struct name_entry *entry,
+                                struct strbuf *base, int base_offset,
+                                const struct pathspec *ps,
+                                int exclude)
 {
        int i;
        int pathlen, baselen = base->len - base_offset;
@@ -676,7 +677,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -697,6 +699,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
                const char *base_str = base->buf + base_offset;
                int matchlen = item->len, matched = 0;
 
+               if ((!exclude &&   item->magic & PATHSPEC_EXCLUDE) ||
+                   ( exclude && !(item->magic & PATHSPEC_EXCLUDE)))
+                       continue;
+
                if (baselen >= matchlen) {
                        /* If it doesn't match, move along... */
                        if (!match_dir_prefix(item, base_str, match, matchlen))
@@ -782,3 +788,72 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
        }
        return never_interesting; /* No matches */
 }
+
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * or base[baselen-1] == '/' (i.e. with trailing slash).
+ */
+enum interesting tree_entry_interesting(const struct name_entry *entry,
+                                       struct strbuf *base, int base_offset,
+                                       const struct pathspec *ps)
+{
+       enum interesting positive, negative;
+       positive = do_match(entry, base, base_offset, ps, 0);
+
+       /*
+        * case | entry | positive | negative | result
+        * -----+-------+----------+----------+-------
+        *   1  |  file |   -1     |  -1..2   |  -1
+        *   2  |  file |    0     |  -1..2   |   0
+        *   3  |  file |    1     |   -1     |   1
+        *   4  |  file |    1     |    0     |   1
+        *   5  |  file |    1     |    1     |   0
+        *   6  |  file |    1     |    2     |   0
+        *   7  |  file |    2     |   -1     |   2
+        *   8  |  file |    2     |    0     |   2
+        *   9  |  file |    2     |    1     |   0
+        *  10  |  file |    2     |    2     |  -1
+        * -----+-------+----------+----------+-------
+        *  11  |  dir  |   -1     |  -1..2   |  -1
+        *  12  |  dir  |    0     |  -1..2   |   0
+        *  13  |  dir  |    1     |   -1     |   1
+        *  14  |  dir  |    1     |    0     |   1
+        *  15  |  dir  |    1     |    1     |   1 (*)
+        *  16  |  dir  |    1     |    2     |   0
+        *  17  |  dir  |    2     |   -1     |   2
+        *  18  |  dir  |    2     |    0     |   2
+        *  19  |  dir  |    2     |    1     |   1 (*)
+        *  20  |  dir  |    2     |    2     |  -1
+        *
+        * (*) An exclude pattern interested in a directory does not
+        * necessarily mean it will exclude all of the directory. In
+        * wildcard case, it can't decide until looking at individual
+        * files inside. So don't write such directories off yet.
+        */
+
+       if (!(ps->magic & PATHSPEC_EXCLUDE) ||
+           positive <= entry_not_interesting) /* #1, #2, #11, #12 */
+               return positive;
+
+       negative = do_match(entry, base, base_offset, ps, 1);
+
+       /* #3, #4, #7, #8, #13, #14, #17, #18 */
+       if (negative <= entry_not_interesting)
+               return positive;
+
+       /* #15, #19 */
+       if (S_ISDIR(entry->mode) &&
+           positive >= entry_interesting &&
+           negative == entry_interesting)
+               return entry_interesting;
+
+       if ((positive == entry_interesting &&
+            negative >= entry_interesting) || /* #5, #6, #16 */
+           (positive == all_entries_interesting &&
+            negative == entry_interesting)) /* #9 */
+               return entry_not_interesting;
+
+       return all_entries_not_interesting; /* #10, #20 */
+}
index d30f339e707fe3263b9c186e54b6b3317afc986c..ec56cdbce9fe0bf87aeafb3567e09cbf546e7218 100644 (file)
@@ -394,7 +394,7 @@ static int get_common_commits(void)
                        got_other = 0;
                        continue;
                }
-               if (!prefixcmp(line, "have ")) {
+               if (starts_with(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
                                got_other = 1;
@@ -540,7 +540,7 @@ static void receive_needs(void)
                if (!line)
                        break;
 
-               if (!prefixcmp(line, "shallow ")) {
+               if (starts_with(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
                        if (get_sha1_hex(line + 8, sha1))
@@ -556,14 +556,14 @@ static void receive_needs(void)
                        }
                        continue;
                }
-               if (!prefixcmp(line, "deepen ")) {
+               if (starts_with(line, "deepen ")) {
                        char *end;
                        depth = strtol(line + 7, &end, 0);
                        if (end == line + 7 || depth <= 0)
                                die("Invalid deepen: %s", line);
                        continue;
                }
-               if (prefixcmp(line, "want ") ||
+               if (!starts_with(line, "want ") ||
                    get_sha1_hex(line+5, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
@@ -814,7 +814,7 @@ int main(int argc, char **argv)
                        strict = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--timeout=")) {
+               if (starts_with(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        daemon_mode = 1;
                        continue;
index f2b23c81de11a63c2c1c2ac925be9081ad5bea5c..bd0f2c2b86847708e5ebe668773f0fd08b9efc5d 100644 (file)
@@ -162,22 +162,13 @@ static void die_short_read(struct line_buffer *input)
        die("invalid dump: unexpected end of file");
 }
 
-static int ends_with(const char *s, size_t len, const char *suffix)
-{
-       const size_t suffixlen = strlen(suffix);
-       if (len < suffixlen)
-               return 0;
-       return !memcmp(s + len - suffixlen, suffix, suffixlen);
-}
-
 static int parse_cat_response_line(const char *header, off_t *len)
 {
-       size_t headerlen = strlen(header);
        uintmax_t n;
        const char *type;
        const char *end;
 
-       if (ends_with(header, headerlen, " missing"))
+       if (ends_with(header, " missing"))
                return error("cat-blob reports missing blob: %s", header);
        type = strstr(header, " blob ");
        if (!type)
index 4625cdb900f179e94314348340719b677fed1ca1..4e5581005936a1e7832e3ab224cfe142ac777a63 100644 (file)
@@ -17,6 +17,9 @@
 #include "strbuf.h"
 #include "utf8.h"
 
+static char cut_line[] =
+"------------------------ >8 ------------------------\n";
+
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
        GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
@@ -793,6 +796,18 @@ static void wt_status_print_other(struct wt_status *s,
        status_printf_ln(s, GIT_COLOR_NORMAL, "");
 }
 
+void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
+{
+       const char *p;
+       struct strbuf pattern = STRBUF_INIT;
+
+       strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
+       p = strstr(buf->buf, pattern.buf);
+       if (p && (p == buf->buf || p[-1] == '\n'))
+               strbuf_setlen(buf, p - buf->buf);
+       strbuf_release(&pattern);
+}
+
 static void wt_status_print_verbose(struct wt_status *s)
 {
        struct rev_info rev;
@@ -813,10 +828,20 @@ static void wt_status_print_verbose(struct wt_status *s)
         * If we're not going to stdout, then we definitely don't
         * want color, since we are going to the commit message
         * file (and even the "auto" setting won't work, since it
-        * will have checked isatty on stdout).
+        * will have checked isatty on stdout). But we then do want
+        * to insert the scissor line here to reliably remove the
+        * diff before committing.
         */
-       if (s->fp != stdout)
+       if (s->fp != stdout) {
+               const char *explanation = _("Do not touch the line above.\nEverything below will be removed.");
+               struct strbuf buf = STRBUF_INIT;
+
                rev.diffopt.use_color = 0;
+               fprintf(s->fp, "%c %s", comment_line_char, cut_line);
+               strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
+               fputs(buf.buf, s->fp);
+               strbuf_release(&buf);
+       }
        run_diff_index(&rev, 1);
 }
 
@@ -829,7 +854,7 @@ static void wt_status_print_tracking(struct wt_status *s)
        int i;
 
        assert(s->branch && !s->is_initial);
-       if (prefixcmp(s->branch, "refs/heads/"))
+       if (!starts_with(s->branch, "refs/heads/"))
                return;
        branch = branch_get(s->branch + 11);
        if (!format_tracking_info(branch, &sb))
@@ -1088,9 +1113,9 @@ static char *read_and_strip_branch(const char *path)
                strbuf_setlen(&sb, sb.len - 1);
        if (!sb.len)
                goto got_nothing;
-       if (!prefixcmp(sb.buf, "refs/heads/"))
+       if (starts_with(sb.buf, "refs/heads/"))
                strbuf_remove(&sb,0, strlen("refs/heads/"));
-       else if (!prefixcmp(sb.buf, "refs/"))
+       else if (starts_with(sb.buf, "refs/"))
                ;
        else if (!get_sha1_hex(sb.buf, sha1)) {
                const char *abbrev;
@@ -1120,7 +1145,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        struct grab_1st_switch_cbdata *cb = cb_data;
        const char *target = NULL, *end;
 
-       if (prefixcmp(message, "checkout: moving from "))
+       if (!starts_with(message, "checkout: moving from "))
                return 0;
        message += strlen("checkout: moving from ");
        target = strstr(message, " to ");
@@ -1155,9 +1180,9 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
             ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
              !hashcmp(cb.nsha1, commit->object.sha1)))) {
                int ofs;
-               if (!prefixcmp(ref, "refs/tags/"))
+               if (starts_with(ref, "refs/tags/"))
                        ofs = strlen("refs/tags/");
-               else if (!prefixcmp(ref, "refs/remotes/"))
+               else if (starts_with(ref, "refs/remotes/"))
                        ofs = strlen("refs/remotes/");
                else
                        ofs = 0;
@@ -1246,7 +1271,7 @@ void wt_status_print(struct wt_status *s)
        if (s->branch) {
                const char *on_what = _("On branch ");
                const char *branch_name = s->branch;
-               if (!prefixcmp(branch_name, "refs/heads/"))
+               if (starts_with(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
@@ -1447,7 +1472,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                return;
        branch_name = s->branch;
 
-       if (!prefixcmp(branch_name, "refs/heads/"))
+       if (starts_with(branch_name, "refs/heads/"))
                branch_name += 11;
        else if (!strcmp(branch_name, "HEAD")) {
                branch_name = _("HEAD (no branch)");
index 6c29e6f5e57f84eb7acdc3cf1ee621cc717fd5a9..30a481258366733ad5d6a65e3a9c3ae1ee74e833 100644 (file)
@@ -91,6 +91,7 @@ struct wt_status_state {
        unsigned char cherry_pick_head_sha1[20];
 };
 
+void wt_status_truncate_message_at_cut_line(struct strbuf *);
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);