aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/diff-config.txt5
-rw-r--r--Documentation/diff-options.txt3
-rw-r--r--Documentation/git-filter-branch.txt33
-rw-r--r--Documentation/git-pack-objects.txt3
-rw-r--r--Documentation/git-push.txt9
-rw-r--r--Documentation/git-rebase.txt10
-rw-r--r--Documentation/git-replace.txt19
-rw-r--r--Documentation/git.txt9
-rw-r--r--Documentation/gitmodules.txt2
-rw-r--r--Documentation/glossary-content.txt34
-rw-r--r--abspath.c16
-rw-r--r--builtin/add.c7
-rw-r--r--builtin/cat-file.c32
-rw-r--r--builtin/diff.c58
-rw-r--r--builtin/fetch.c42
-rw-r--r--builtin/for-each-ref.c3
-rw-r--r--builtin/prune-packed.c38
-rw-r--r--builtin/prune.c38
-rw-r--r--builtin/push.c84
-rw-r--r--builtin/replace.c67
-rw-r--r--builtin/rev-parse.c16
-rw-r--r--cache.h12
-rw-r--r--contrib/credential/gnome-keyring/git-credential-gnome-keyring.c85
-rwxr-xr-xcontrib/remote-helpers/git-remote-bzr14
-rwxr-xr-xcontrib/remote-helpers/git-remote-hg19
-rwxr-xr-xcontrib/remote-helpers/test-hg.sh79
-rw-r--r--csum-file.c6
-rw-r--r--csum-file.h2
-rw-r--r--daemon.c4
-rw-r--r--diff-no-index.c44
-rw-r--r--diff.c25
-rw-r--r--diff.h4
-rw-r--r--diffcore-order.c34
-rw-r--r--dir.c47
-rwxr-xr-xgit-difftool--helper.sh3
-rwxr-xr-xgit-pull.sh10
-rwxr-xr-xgit-rebase.sh19
-rw-r--r--log-tree.c2
-rw-r--r--pack-write.c8
-rw-r--r--pack.h2
-rw-r--r--pathspec.c37
-rw-r--r--pathspec.h4
-rw-r--r--perl/Git/SVN/Editor.pm10
-rw-r--r--po/de.po16
-rw-r--r--remote.c8
-rw-r--r--remote.h2
-rw-r--r--replace_object.c3
-rw-r--r--sha1_file.c57
-rw-r--r--streaming.c2
-rwxr-xr-xt/perf/p4001-diff-no-index.sh22
-rwxr-xr-xt/t1006-cat-file.sh22
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh24
-rwxr-xr-xt/t3400-rebase.sh6
-rwxr-xr-xt/t3700-add.sh19
-rwxr-xr-xt/t4020-diff-external.sh13
-rwxr-xr-xt/t4053-diff-no-index.sh26
-rwxr-xr-xt/t4056-diff-order.sh104
-rwxr-xr-xt/t4205-log-pretty-formats.sh15
-rwxr-xr-xt/t5302-pack-index.sh4
-rwxr-xr-xt/t5510-fetch.sh26
-rwxr-xr-xt/t5516-fetch-push.sh75
-rwxr-xr-xt/t6050-replace.sh42
-rwxr-xr-xt/t6132-pathspec-exclude.sh184
-rw-r--r--tree-walk.c83
-rw-r--r--unpack-trees.c51
65 files changed, 1447 insertions, 355 deletions
diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
index 223b9310d..f07b4513e 100644
--- a/Documentation/diff-config.txt
+++ b/Documentation/diff-config.txt
@@ -98,6 +98,11 @@ diff.mnemonicprefix::
diff.noprefix::
If set, 'git diff' does not show any source or destination prefix.
+diff.orderfile::
+ File indicating how to order files within a diff, using
+ one shell glob pattern per line.
+ Can be overridden by the '-O' option to linkgit:git-diff[1].
+
diff.renameLimit::
The number of files to consider when performing the copy/rename
detection; equivalent to the 'git diff' option '-l'.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index bbed2cd79..9b37b2a10 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -432,6 +432,9 @@ endif::git-format-patch[]
-O<orderfile>::
Output the patch in the order specified in the
<orderfile>, which has one shell glob pattern per line.
+ This overrides the `diff.orderfile` configuration variable
+ (see linkgit:git-config[1]). To cancel `diff.orderfile`,
+ use `-O/dev/null`.
ifndef::git-format-patch[]
-R::
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index e4c8e8266..2eba62717 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -393,7 +393,7 @@ git filter-branch --index-filter \
Checklist for Shrinking a Repository
------------------------------------
-git-filter-branch is often used to get rid of a subset of files,
+git-filter-branch can be used to get rid of a subset of files,
usually with some combination of `--index-filter` and
`--subdirectory-filter`. People expect the resulting repository to
be smaller than the original, but you need a few more steps to
@@ -429,6 +429,37 @@ warned.
(or if your git-gc is not new enough to support arguments to
`--prune`, use `git repack -ad; git prune` instead).
+Notes
+-----
+
+git-filter-branch allows you to make complex shell-scripted rewrites
+of your Git history, but you probably don't need this flexibility if
+you're simply _removing unwanted data_ like large files or passwords.
+For those operations you may want to consider
+link:http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner],
+a JVM-based alternative to git-filter-branch, typically at least
+10-50x faster for those use-cases, and with quite different
+characteristics:
+
+* Any particular version of a file is cleaned exactly _once_. The BFG,
+ unlike git-filter-branch, does not give you the opportunity to
+ handle a file differently based on where or when it was committed
+ within your history. This constraint gives the core performance
+ benefit of The BFG, and is well-suited to the task of cleansing bad
+ data - you don't care _where_ the bad data is, you just want it
+ _gone_.
+
+* By default The BFG takes full advantage of multi-core machines,
+ cleansing commit file-trees in parallel. git-filter-branch cleans
+ commits sequentially (ie in a single-threaded manner), though it
+ _is_ possible to write filters that include their own parallellism,
+ in the scripts executed against each commit.
+
+* The link:http://rtyley.github.io/bfg-repo-cleaner/#examples[command options]
+ are much more restrictive than git-filter branch, and dedicated just
+ to the tasks of removing unwanted data- e.g:
+ `--strip-blobs-bigger-than 1M`.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index d94edcd4b..cdab9ed50 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -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::
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 9eec74091..2b7f4f939 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -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
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 94e07fdab..2889be6bd 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -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
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index f373ab48d..0a02f7065 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -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
----------------------------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index cc0e5e2bf..aec372646 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -807,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
~~~~~
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index f7be93f63..347a9f76e 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -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
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index aa1c8880d..378306f58 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -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
diff --git a/abspath.c b/abspath.c
index 8b3385a77..ca33558a9 100644
--- a/abspath.c
+++ b/abspath.c
@@ -215,23 +215,25 @@ const char *absolute_path(const char *path)
*/
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
- static char path[PATH_MAX];
+ static struct strbuf path = STRBUF_INIT;
#ifndef GIT_WINDOWS_NATIVE
if (!pfx_len || is_absolute_path(arg))
return arg;
- memcpy(path, pfx, pfx_len);
- strcpy(path + pfx_len, arg);
+ strbuf_reset(&path);
+ strbuf_add(&path, pfx, pfx_len);
+ strbuf_addstr(&path, arg);
#else
char *p;
/* don't add prefix to absolute paths, but still replace '\' by '/' */
+ strbuf_reset(&path);
if (is_absolute_path(arg))
pfx_len = 0;
else if (pfx_len)
- memcpy(path, pfx, pfx_len);
- strcpy(path + pfx_len, arg);
- for (p = path + pfx_len; *p; p++)
+ strbuf_add(&path, pfx, pfx_len);
+ strbuf_addstr(&path, arg);
+ for (p = path.buf + pfx_len; *p; p++)
if (*p == '\\')
*p = '/';
#endif
- return path;
+ return path.buf;
}
diff --git a/builtin/add.c b/builtin/add.c
index 226f75886..2a2722fa1 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -540,11 +540,14 @@ 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 (!seen[i] &&
+ if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+ continue;
+ if (!seen[i] && path[0] &&
((pathspec.items[i].magic &
(PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
!file_exists(path))) {
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index b2ca775a8..f12071adc 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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;
@@ -275,6 +278,13 @@ static int batch_objects(struct batch_options *opt)
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
* cost to double-check that each one is not also a ref (just so we can
diff --git a/builtin/diff.c b/builtin/diff.c
index fe0cc7f1b..0f247d240 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -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;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 1e7d617f4..09825c84d 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -44,6 +44,7 @@ static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = "";
static const char *recurse_submodules_default;
+static int shown_url = 0;
static int option_parse_recurse_submodules(const struct option *opt,
const char *arg, int unset)
@@ -535,7 +536,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
{
FILE *fp;
struct commit *commit;
- int url_len, i, shown_url = 0, rc = 0;
+ int url_len, i, rc = 0;
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
@@ -708,17 +709,36 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
+ const char *raw_url)
{
- int result = 0;
+ int url_len, i, result = 0;
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+ char *url;
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
+ if (raw_url)
+ url = transport_anonymize_url(raw_url);
+ else
+ url = xstrdup("foreign");
+
+ url_len = strlen(url);
+ for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+ ;
+
+ url_len = i + 1;
+ if (4 < i && !strncmp(".git", url + i - 3, 4))
+ url_len = i - 3;
+
for (ref = stale_refs; ref; ref = ref->next) {
if (!dry_run)
result |= delete_ref(ref->name, NULL, 0);
+ if (verbosity >= 0 && !shown_url) {
+ fprintf(stderr, _("From %.*s\n"), url_len, url);
+ shown_url = 1;
+ }
if (verbosity >= 0) {
fprintf(stderr, " x %-*s %-*s -> %s\n",
TRANSPORT_SUMMARY(_("[deleted]")),
@@ -726,6 +746,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
+ free(url);
free_refs(stale_refs);
return result;
}
@@ -842,11 +863,6 @@ static int do_fetch(struct transport *transport,
if (tags == TAGS_DEFAULT && autotags)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
- if (fetch_refs(transport, ref_map)) {
- free_refs(ref_map);
- retcode = 1;
- goto cleanup;
- }
if (prune) {
/*
* We only prune based on refspecs specified
@@ -854,13 +870,19 @@ static int do_fetch(struct transport *transport,
* don't care whether --tags was specified.
*/
if (ref_count) {
- prune_refs(refs, ref_count, ref_map);
+ prune_refs(refs, ref_count, ref_map, transport->url);
} else {
prune_refs(transport->remote->fetch,
transport->remote->fetch_refspec_nr,
- ref_map);
+ ref_map,
+ transport->url);
}
}
+ if (fetch_refs(transport, ref_map)) {
+ free_refs(ref_map);
+ retcode = 1;
+ goto cleanup;
+ }
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 6551e7b39..51798b48b 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -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;
/*
@@ -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);
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index fa6ce42f4..fcf5fb612 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = {
static struct progress *progress;
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
+static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts)
{
struct dirent *de;
char hex[40];
+ int top_len = pathname->len;
sprintf(hex, "%02x", i);
while ((de = readdir(dir)) != NULL) {
unsigned char sha1[20];
if (strlen(de->d_name) != 38)
continue;
- memcpy(hex+2, de->d_name, 38);
+ memcpy(hex + 2, de->d_name, 38);
if (get_sha1_hex(hex, sha1))
continue;
if (!has_sha1_pack(sha1))
continue;
- memcpy(pathname + len, de->d_name, 38);
+
+ strbuf_add(pathname, de->d_name, 38);
if (opts & PRUNE_PACKED_DRY_RUN)
- printf("rm -f %s\n", pathname);
+ printf("rm -f %s\n", pathname->buf);
else
- unlink_or_warn(pathname);
+ unlink_or_warn(pathname->buf);
display_progress(progress, i + 1);
+ strbuf_setlen(pathname, top_len);
}
}
void prune_packed_objects(int opts)
{
int i;
- static char pathname[PATH_MAX];
const char *dir = get_object_directory();
- int len = strlen(dir);
+ struct strbuf pathname = STRBUF_INIT;
+ int top_len;
+ strbuf_addstr(&pathname, dir);
if (opts & PRUNE_PACKED_VERBOSE)
progress = start_progress_delay("Removing duplicate objects",
256, 95, 2);
- if (len > PATH_MAX - 42)
- die("impossible object directory");
- memcpy(pathname, dir, len);
- if (len && pathname[len-1] != '/')
- pathname[len++] = '/';
+ if (pathname.len && pathname.buf[pathname.len - 1] != '/')
+ strbuf_addch(&pathname, '/');
+
+ top_len = pathname.len;
for (i = 0; i < 256; i++) {
DIR *d;
display_progress(progress, i + 1);
- sprintf(pathname + len, "%02x/", i);
- d = opendir(pathname);
+ strbuf_setlen(&pathname, top_len);
+ strbuf_addf(&pathname, "%02x/", i);
+ d = opendir(pathname.buf);
if (!d)
continue;
- prune_dir(i, d, pathname, len + 3, opts);
+ prune_dir(i, d, &pathname, opts);
closedir(d);
- pathname[len + 2] = '\0';
- rmdir(pathname);
+ strbuf_setlen(&pathname, top_len + 2);
+ rmdir(pathname.buf);
}
stop_progress(&progress);
}
diff --git a/builtin/prune.c b/builtin/prune.c
index a85ef54d5..beb0dc942 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -17,9 +17,8 @@ static int verbose;
static unsigned long expire;
static int show_progress = -1;
-static int prune_tmp_object(const char *path, const char *filename)
+static int prune_tmp_file(const char *fullpath)
{
- const char *fullpath = mkpath("%s/%s", path, filename);
struct stat st;
if (lstat(fullpath, &st))
return error("Could not stat '%s'", fullpath);
@@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename)
return 0;
}
-static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+static int prune_object(const char *fullpath, const unsigned char *sha1)
{
- const char *fullpath = mkpath("%s/%s", path, filename);
struct stat st;
if (lstat(fullpath, &st))
return error("Could not stat '%s'", fullpath);
@@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
return 0;
}
-static int prune_dir(int i, char *path)
+static int prune_dir(int i, struct strbuf *path)
{
- DIR *dir = opendir(path);
+ size_t baselen = path->len;
+ DIR *dir = opendir(path->buf);
struct dirent *de;
if (!dir)
@@ -77,28 +76,39 @@ static int prune_dir(int i, char *path)
if (lookup_object(sha1))
continue;
- prune_object(path, de->d_name, sha1);
+ strbuf_addf(path, "/%s", de->d_name);
+ prune_object(path->buf, sha1);
+ strbuf_setlen(path, baselen);
continue;
}
if (starts_with(de->d_name, "tmp_obj_")) {
- prune_tmp_object(path, de->d_name);
+ strbuf_addf(path, "/%s", de->d_name);
+ prune_tmp_file(path->buf);
+ strbuf_setlen(path, baselen);
continue;
}
- fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+ fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
}
closedir(dir);
if (!show_only)
- rmdir(path);
+ rmdir(path->buf);
return 0;
}
static void prune_object_dir(const char *path)
{
+ struct strbuf buf = STRBUF_INIT;
+ size_t baselen;
int i;
+
+ strbuf_addstr(&buf, path);
+ strbuf_addch(&buf, '/');
+ baselen = buf.len;
+
for (i = 0; i < 256; i++) {
- static char dir[4096];
- sprintf(dir, "%s/%02x", path, i);
- prune_dir(i, dir);
+ strbuf_addf(&buf, "%02x", i);
+ prune_dir(i, &buf);
+ strbuf_setlen(&buf, baselen);
}
}
@@ -120,7 +130,7 @@ static void remove_temporary_files(const char *path)
}
while ((de = readdir(dir)) != NULL)
if (starts_with(de->d_name, "tmp_"))
- prune_tmp_object(path, de->d_name);
+ prune_tmp_file(mkpath("%s/%s", path, de->d_name));
closedir(dir);
}
diff --git a/builtin/push.c b/builtin/push.c
index a73982a30..0e50ddbb0 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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);
diff --git a/builtin/replace.c b/builtin/replace.c
index b1bd3ef99..2336325ce 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -16,27 +16,69 @@
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);
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 6e802fdeb..aaeb611a9 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -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++) {
@@ -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;
diff --git a/cache.h b/cache.h
index ce377e135..9ba9773ed 100644
--- 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);
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
index 635c96bc5..2a317fca4 100644
--- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
+++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
@@ -60,7 +60,7 @@
#define gnome_keyring_memory_free gnome_keyring_free_password
#define gnome_keyring_memory_strdup g_strdup
-static const char* gnome_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 char* keyring_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)
{
- char* object = 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]);
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 7e345320a..332aba784 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -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:
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 30402d553..eb89ef677 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -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
diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh
index 347e81292..5d128a5da 100755
--- a/contrib/remote-helpers/test-hg.sh
+++ b/contrib/remote-helpers/test-hg.sh
@@ -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 &&
diff --git a/csum-file.c b/csum-file.c
index b30e4f289..b00b21503 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -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;
}
-void sha1write(struct sha1file *f, void *buf, unsigned int count)
+void 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);
diff --git a/csum-file.h b/csum-file.h
index 6a55c7d72..bb543d52f 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -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 void sha1write(struct sha1file *, void *, unsigned int);
+extern void 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 *);
diff --git a/daemon.c b/daemon.c
index 7bee953de..503e03906 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1278,11 +1278,11 @@ int main(int argc, char **argv)
make_service_overridable(arg + 18, 0);
continue;
}
- if (starts_with(arg, "--informative-errors")) {
+ if (!strcmp(arg, "--informative-errors")) {
informative_errors = 1;
continue;
}
- if (starts_with(arg, "--no-informative-errors")) {
+ if (!strcmp(arg, "--no-informative-errors")) {
informative_errors = 0;
continue;
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 00a8eefde..33e5982a1 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -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 b79432b1e..6b4cd0eb3 100644
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static int diff_use_color_default = -1;
static int diff_context_default = 3;
static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
+static const char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
@@ -201,6 +202,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return git_config_string(&external_diff_cmd_cfg, var, value);
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
+ if (!strcmp(var, "diff.orderfile"))
+ return git_config_pathname(&diff_order_file_cfg, var, value);
if (!strcmp(var, "diff.ignoresubmodules"))
handle_ignore_submodules_arg(&default_diff_options, value);
@@ -2899,11 +2902,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 +2936,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 +3057,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)
@@ -3207,6 +3222,8 @@ void diff_setup(struct diff_options *options)
options->detect_rename = diff_detect_rename_default;
options->xdl_opts |= diff_algorithm;
+ options->orderfile = diff_order_file_cfg;
+
if (diff_no_prefix) {
options->a_prefix = options->b_prefix = "";
} else if (!diff_mnemonic_prefix) {
@@ -3317,6 +3334,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)
diff --git a/diff.h b/diff.h
index 1c05b3a6b..ce123fa06 100644
--- 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/diffcore-order.c b/diffcore-order.c
index 23e93852d..fe7f1f464 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -10,28 +10,21 @@ static int order_cnt;
static void prepare_order(const char *orderfile)
{
- int fd, cnt, pass;
+ int cnt, pass;
+ struct strbuf sb = STRBUF_INIT;
void *map;
char *cp, *endp;
- struct stat st;
- size_t sz;
+ ssize_t sz;
if (order)
return;
- fd = open(orderfile, O_RDONLY);
- if (fd < 0)
- return;
- if (fstat(fd, &st)) {
- close(fd);
- return;
- }
- sz = xsize_t(st.st_size);
- map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
- close(fd);
- if (map == MAP_FAILED)
- return;
+ sz = strbuf_read_file(&sb, orderfile, 0);
+ if (sz < 0)
+ die_errno(_("failed to read orderfile '%s'"), orderfile);
+ map = strbuf_detach(&sb, NULL);
endp = (char *) map + sz;
+
for (pass = 0; pass < 2; pass++) {
cnt = 0;
cp = map;
@@ -73,15 +66,16 @@ struct pair_order {
static int match_order(const char *path)
{
int i;
- char p[PATH_MAX];
+ static struct strbuf p = STRBUF_INIT;
for (i = 0; i < order_cnt; i++) {
- strcpy(p, path);
- while (p[0]) {
+ strbuf_reset(&p);
+ strbuf_addstr(&p, path);
+ while (p.buf[0]) {
char *cp;
- if (!fnmatch(order[i], p, 0))
+ if (!fnmatch(order[i], p.buf, 0))
return i;
- cp = strrchr(p, '/');
+ cp = strrchr(p.buf, '/');
if (!cp)
break;
*cp = 0;
diff --git a/dir.c b/dir.c
index 23b6de470..d10a63f73 100644
--- 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);
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index b00ed95db..7ef36b948 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -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]: " \
diff --git a/git-pull.sh b/git-pull.sh
index 97716b885..f210d0a14 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -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
diff --git a/git-rebase.sh b/git-rebase.sh
index 226752fbf..7185dc843 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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") ||
diff --git a/log-tree.c b/log-tree.c
index 642faffdb..08970bf46 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -134,6 +134,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
+ if (!obj->parsed)
+ parse_object(obj->sha1);
add_name_decoration(DECORATION_REF_TAG, refname, obj);
}
return 0;
diff --git a/pack-write.c b/pack-write.c
index 676ed4ce9..605d01b25 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -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 aa6ee7d60..12d951659 100644
--- 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/pathspec.c b/pathspec.c
index 52d38a4b4..6cb9fd301 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -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.
@@ -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)
diff --git a/pathspec.h b/pathspec.h
index a75e9242d..0c1126264 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -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 */
diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm
index b3bcd476d..34e8af966 100644
--- a/perl/Git/SVN/Editor.pm
+++ b/perl/Git/SVN/Editor.pm
@@ -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);
diff --git a/po/de.po b/po/de.po
index 5e2d709e1..c0bbb65bf 100644
--- 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"
diff --git a/remote.c b/remote.c
index 709a9cbd2..a89efab5b 100644
--- a/remote.c
+++ b/remote.c
@@ -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;
diff --git a/remote.h b/remote.h
index c07eb9950..00c6a76ef 100644
--- 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);
diff --git a/replace_object.c b/replace_object.c
index d0b154872..cdcaf8cbe 100644
--- a/replace_object.c
+++ b/replace_object.c
@@ -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 */
diff --git a/sha1_file.c b/sha1_file.c
index daacc0cfb..a2ff2961b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -807,15 +807,38 @@ void free_pack_by_name(const char *pack_name)
static unsigned int get_max_fd_limit(void)
{
#ifdef RLIMIT_NOFILE
- struct rlimit lim;
+ {
+ struct rlimit lim;
- if (getrlimit(RLIMIT_NOFILE, &lim))
- die_errno("cannot get RLIMIT_NOFILE");
+ if (!getrlimit(RLIMIT_NOFILE, &lim))
+ return lim.rlim_cur;
+ }
+#endif
- return lim.rlim_cur;
-#elif defined(_SC_OPEN_MAX)
- return sysconf(_SC_OPEN_MAX);
-#elif defined(OPEN_MAX)
+#ifdef _SC_OPEN_MAX
+ {
+ long open_max = sysconf(_SC_OPEN_MAX);
+ if (0 < open_max)
+ return open_max;
+ /*
+ * Otherwise, we got -1 for one of the two
+ * reasons:
+ *
+ * (1) sysconf() did not understand _SC_OPEN_MAX
+ * and signaled an error with -1; or
+ * (2) sysconf() said there is no limit.
+ *
+ * We _could_ clear errno before calling sysconf() to
+ * tell these two cases apart and return a huge number
+ * in the latter case to let the caller cap it to a
+ * value that is not so selfish, but letting the
+ * fallback OPEN_MAX codepath take care of these cases
+ * is a lot simpler.
+ */
+ }
+#endif
+
+#ifdef OPEN_MAX
return OPEN_MAX;
#else
return 1; /* see the caller ;-) */
@@ -2443,13 +2466,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 +2485,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 +2523,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 +2615,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);
diff --git a/streaming.c b/streaming.c
index debe90452..9659f18be 100644
--- a/streaming.c
+++ b/streaming.c
@@ -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;
diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh
new file mode 100755
index 000000000..683be6984
--- /dev/null
+++ b/t/perf/p4001-diff-no-index.sh
@@ -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
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 8a1bc5c53..168709835 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -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"
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index f950c1012..613d9bfe1 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -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
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index ebf93b069..998503db1 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -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' '
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index aab86e838..fe274e2fb 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -272,6 +272,25 @@ test_expect_success '"add non-existent" should fail' '
! (git ls-files | grep "non-existent")
'
+test_expect_success 'git add -A on empty repo does not error out' '
+ rm -fr empty &&
+ git init empty &&
+ (
+ cd empty &&
+ git add -A . &&
+ git add -A
+ )
+'
+
+test_expect_success '"git add ." in empty repo' '
+ rm -fr empty &&
+ git init empty &&
+ (
+ cd empty &&
+ git add .
+ )
+'
+
test_expect_success 'git add --dry-run of existing changed file' "
echo new >>track-this &&
git add --dry-run track-this >actual 2>&1 &&
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 8a309795c..bcae35ac1 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -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 &&
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 979e98398..2ab3c4873 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -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
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
new file mode 100755
index 000000000..1ddd226b7
--- /dev/null
+++ b/t/t4056-diff-order.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='diff order'
+
+. ./test-lib.sh
+
+create_files () {
+ echo "$1" >a.h &&
+ echo "$1" >b.c &&
+ echo "$1" >c/Makefile &&
+ echo "$1" >d.txt &&
+ git add a.h b.c c/Makefile d.txt &&
+ git commit -m"$1"
+}
+
+test_expect_success 'setup' '
+ mkdir c &&
+ create_files 1 &&
+ create_files 2 &&
+
+ cat >order_file_1 <<-\EOF &&
+ *Makefile
+ *.txt
+ *.h
+ EOF
+
+ cat >order_file_2 <<-\EOF &&
+ *Makefile
+ *.h
+ *.c
+ EOF
+
+ cat >expect_none <<-\EOF &&
+ a.h
+ b.c
+ c/Makefile
+ d.txt
+ EOF
+
+ cat >expect_1 <<-\EOF &&
+ c/Makefile
+ d.txt
+ a.h
+ b.c
+ EOF
+
+ cat >expect_2 <<-\EOF
+ c/Makefile
+ a.h
+ b.c
+ d.txt
+ EOF
+'
+
+test_expect_success "no order (=tree object order)" '
+ git diff --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_none actual
+'
+
+test_expect_success 'missing orderfile' '
+ rm -f bogus_file &&
+ test_must_fail git diff -Obogus_file --name-only HEAD^..HEAD
+'
+
+test_expect_success POSIXPERM,SANITY 'unreadable orderfile' '
+ >unreadable_file &&
+ chmod -r unreadable_file &&
+ test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD
+'
+
+test_expect_success 'orderfile is a directory' '
+ test_must_fail git diff -O/ --name-only HEAD^..HEAD
+'
+
+for i in 1 2
+do
+ test_expect_success "orderfile using option ($i)" '
+ git diff -Oorder_file_$i --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success PIPE "orderfile is fifo ($i)" '
+ rm -f order_fifo &&
+ mkfifo order_fifo &&
+ {
+ cat order_file_$i >order_fifo &
+ } &&
+ git diff -O order_fifo --name-only HEAD^..HEAD >actual &&
+ wait &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success "orderfile using config ($i)" '
+ git -c diff.orderfile=order_file_$i diff --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success "cancelling configured orderfile ($i)" '
+ git -c diff.orderfile=order_file_$i diff -O/dev/null --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_none actual
+ '
+done
+
+test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index fb0004113..2a6278bb3 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -310,4 +310,19 @@ EOF
test_cmp expected actual
'
+test_expect_success 'log decoration properly follows tag chain' '
+ git tag -a tag1 -m tag1 &&
+ git tag -a tag2 -m tag2 tag1 &&
+ git tag -d tag1 &&
+ git commit --amend -m shorter &&
+ git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
+ cat <<EOF >expected &&
+6a908c10688b2503073c39c9ba26322c73902bb5 (tag: refs/tags/tag2)
+9f716384d92283fb915a4eee5073f030638e05f9 (tag: refs/tags/message-one)
+b87e4cccdb77336ea79d89224737be7ea8e95367 (tag: refs/tags/message-two)
+EOF
+ sort actual >actual1 &&
+ test_cmp expected actual1
+'
+
test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index fe82025d4..4bbb71875 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -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' \
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 5d4581dac..12674ac09 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -614,4 +614,30 @@ test_expect_success 'all boundary commits are excluded' '
test_bundle_object_count .git/objects/pack/pack-${pack##pack }.pack 3
'
+test_expect_success 'fetch --prune prints the remotes url' '
+ git branch goodbye &&
+ git clone . only-prunes &&
+ git branch -D goodbye &&
+ (
+ cd only-prunes &&
+ git fetch --prune origin 2>&1 | head -n1 >../actual
+ ) &&
+ echo "From ${D}/." >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'branchname D/F conflict resolved by --prune' '
+ git branch dir/file &&
+ git clone . prune-df-conflict &&
+ git branch -D dir/file &&
+ git branch dir &&
+ (
+ cd prune-df-conflict &&
+ git fetch --prune &&
+ git rev-parse origin/dir >../actual
+ ) &&
+ git rev-parse dir >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 99c32d753..926e7f6b9 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -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 &&
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 7d4798435..719a11673 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -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
index 000000000..62049be0c
--- /dev/null
+++ b/t/t6132-pathspec-exclude.sh
@@ -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
diff --git a/tree-walk.c b/tree-walk.c
index 5ece8c347..680afda06 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -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 @@ match_wildcards:
}
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 */
+}
diff --git a/unpack-trees.c b/unpack-trees.c
index ad3e9a04f..164354dad 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -830,23 +830,24 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
}
static int clear_ce_flags_1(struct cache_entry **cache, int nr,
- char *prefix, int prefix_len,
+ struct strbuf *prefix,
int select_mask, int clear_mask,
struct exclude_list *el, int defval);
/* Whole directory matching */
static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
- char *prefix, int prefix_len,
+ struct strbuf *prefix,
char *basename,
int select_mask, int clear_mask,
struct exclude_list *el, int defval)
{
struct cache_entry **cache_end;
int dtype = DT_DIR;
- int ret = is_excluded_from_list(prefix, prefix_len,
+ int ret = is_excluded_from_list(prefix->buf, prefix->len,
basename, &dtype, el);
+ int rc;
- prefix[prefix_len++] = '/';
+ strbuf_addch(prefix, '/');
/* If undecided, use matching result of parent dir in defval */
if (ret < 0)
@@ -854,7 +855,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
for (cache_end = cache; cache_end != cache + nr; cache_end++) {
struct cache_entry *ce = *cache_end;
- if (strncmp(ce->name, prefix, prefix_len))
+ if (strncmp(ce->name, prefix->buf, prefix->len))
break;
}
@@ -865,10 +866,12 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
* calling clear_ce_flags_1(). That function will call
* the expensive is_excluded_from_list() on every entry.
*/
- return clear_ce_flags_1(cache, cache_end - cache,
- prefix, prefix_len,
- select_mask, clear_mask,
- el, ret);
+ rc = clear_ce_flags_1(cache, cache_end - cache,
+ prefix,
+ select_mask, clear_mask,
+ el, ret);
+ strbuf_setlen(prefix, prefix->len - 1);
+ return rc;
}
/*
@@ -887,7 +890,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
* Top level path has prefix_len zero.
*/
static int clear_ce_flags_1(struct cache_entry **cache, int nr,
- char *prefix, int prefix_len,
+ struct strbuf *prefix,
int select_mask, int clear_mask,
struct exclude_list *el, int defval)
{
@@ -907,10 +910,10 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
continue;
}
- if (prefix_len && strncmp(ce->name, prefix, prefix_len))
+ if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len))
break;
- name = ce->name + prefix_len;
+ name = ce->name + prefix->len;
slash = strchr(name, '/');
/* If it's a directory, try whole directory match first */
@@ -918,29 +921,26 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
int processed;
len = slash - name;
- memcpy(prefix + prefix_len, name, len);
+ strbuf_add(prefix, name, len);
- /*
- * terminate the string (no trailing slash),
- * clear_c_f_dir needs it
- */
- prefix[prefix_len + len] = '\0';
processed = clear_ce_flags_dir(cache, cache_end - cache,
- prefix, prefix_len + len,
- prefix + prefix_len,
+ prefix,
+ prefix->buf + prefix->len - len,
select_mask, clear_mask,
el, defval);
/* clear_c_f_dir eats a whole dir already? */
if (processed) {
cache += processed;
+ strbuf_setlen(prefix, prefix->len - len);
continue;
}
- prefix[prefix_len + len++] = '/';
+ strbuf_addch(prefix, '/');
cache += clear_ce_flags_1(cache, cache_end - cache,
- prefix, prefix_len + len,
+ prefix,
select_mask, clear_mask, el, defval);
+ strbuf_setlen(prefix, prefix->len - len - 1);
continue;
}
@@ -961,9 +961,12 @@ static int clear_ce_flags(struct cache_entry **cache, int nr,
int select_mask, int clear_mask,
struct exclude_list *el)
{
- char prefix[PATH_MAX];
+ static struct strbuf prefix = STRBUF_INIT;
+
+ strbuf_reset(&prefix);
+
return clear_ce_flags_1(cache, nr,
- prefix, 0,
+ &prefix,
select_mask, clear_mask,
el, 0);
}