summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile2
-rw-r--r--Documentation/RelNotes/1.9.txt115
-rw-r--r--Documentation/config.txt4
-rw-r--r--Documentation/diff-config.txt5
-rw-r--r--Documentation/diff-options.txt3
-rw-r--r--Documentation/git-cat-file.txt12
-rw-r--r--Documentation/git-filter-branch.txt33
-rw-r--r--Documentation/git-replace.txt19
-rw-r--r--Documentation/glossary-content.txt34
-rw-r--r--Documentation/technical/api-builtin.txt4
-rw-r--r--Documentation/user-manual.txt2
-rw-r--r--abspath.c16
-rw-r--r--builtin.h2
-rw-r--r--builtin/add.c7
-rw-r--r--builtin/blame.c8
-rw-r--r--builtin/cat-file.c8
-rw-r--r--builtin/fetch.c42
-rw-r--r--builtin/gc.c2
-rw-r--r--builtin/help.c6
-rw-r--r--builtin/merge-base.c32
-rw-r--r--builtin/pack-objects.c2
-rw-r--r--builtin/prune-packed.c38
-rw-r--r--builtin/prune.c38
-rw-r--r--builtin/replace.c67
-rw-r--r--cache.h13
-rw-r--r--commit.c50
-rw-r--r--commit.h4
-rw-r--r--config.mak.uname7
-rw-r--r--contrib/completion/git-completion.bash25
-rw-r--r--contrib/completion/git-completion.zsh10
-rw-r--r--csum-file.c3
-rw-r--r--csum-file.h2
-rw-r--r--daemon.c4
-rw-r--r--diff.c5
-rw-r--r--diffcore-order.c34
-rw-r--r--dir.c47
-rw-r--r--git-compat-util.h2
-rwxr-xr-xgit-rebase.sh3
-rw-r--r--git-sh-setup.sh3
-rwxr-xr-xgit-stash.sh14
-rw-r--r--git.c256
-rw-r--r--log-tree.c2
-rw-r--r--pack-write.c3
-rw-r--r--pager.c11
-rw-r--r--pathspec.c37
-rw-r--r--pathspec.h4
-rw-r--r--perl/Git/SVN/Log.pm1
-rw-r--r--prompt.c3
-rw-r--r--replace_object.c3
-rw-r--r--sha1_file.c110
-rw-r--r--sha1_name.c4
-rw-r--r--streaming.c2
-rw-r--r--t/annotate-tests.sh21
-rw-r--r--t/lib-httpd.sh15
-rw-r--r--t/lib-httpd/passwd2
-rwxr-xr-xt/t0000-basic.sh19
-rwxr-xr-xt/t1006-cat-file.sh34
-rwxr-xr-xt/t3400-rebase.sh12
-rwxr-xr-xt/t3700-add.sh19
-rwxr-xr-xt/t3903-stash.sh12
-rwxr-xr-xt/t4056-diff-order.sh104
-rwxr-xr-xt/t4205-log-pretty-formats.sh15
-rwxr-xr-xt/t5510-fetch.sh26
-rwxr-xr-xt/t5540-http-push.sh4
-rwxr-xr-xt/t5541-http-push.sh6
-rwxr-xr-xt/t5550-http-fetch.sh10
-rwxr-xr-xt/t5551-http-fetch.sh6
-rwxr-xr-xt/t6010-merge-base.sh39
-rwxr-xr-xt/t6050-replace.sh42
-rwxr-xr-xt/t6101-rev-parse-parents.sh16
-rwxr-xr-xt/t6132-pathspec-exclude.sh184
-rwxr-xr-xt/t7006-pager.sh12
-rw-r--r--t/test-lib.sh2
-rw-r--r--tree-walk.c83
-rw-r--r--unpack-trees.c51
75 files changed, 1498 insertions, 399 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 91a12c7e51..36c58fc646 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -324,7 +324,7 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
user-manual.xml: user-manual.txt user-manual.conf
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book -o $@+ $< && \
+ $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d article -o $@+ $< && \
mv $@+ $@
technical/api-index.txt: technical/api-index-skel.txt \
diff --git a/Documentation/RelNotes/1.9.txt b/Documentation/RelNotes/1.9.txt
index a966ab4887..41a54f5f42 100644
--- a/Documentation/RelNotes/1.9.txt
+++ b/Documentation/RelNotes/1.9.txt
@@ -19,6 +19,10 @@ The meanings of "--tags" option to "git fetch" has changed; the
command fetches tags _in addition to_ what are fetched by the same
command line without the option.
+The way "git push $there $what" interprets $what part given on the
+command line, when it does not have a colon that explicitly tells us
+what ref at the $there repository is to be updated, has been enhanced.
+
A handful of ancient commands that have long been deprecated are
finally gone (repo-config, tar-tree, lost-found, and peek-remote).
@@ -81,9 +85,50 @@ Foreign interfaces, subsystems and ports.
* Various bugfixes to remote-bzr and remote-hg (in contrib/).
+ * The build procedure is aware of MirBSD now.
+
UI, Workflows & Features
+ * Two-level configuration variable names in "branch.*" and "remote.*"
+ hierarchies, whose variables are predominantly three-level, were
+ not completed by hitting a <TAB> in bash and zsh completions.
+
+ * Fetching 'frotz' branch with "git fetch", while 'frotz/nitfol'
+ remote-tracking branch from an earlier fetch was still there, would
+ error out, primarily because the command was not told that it is
+ allowed to lose any information on our side. "git fetch --prune"
+ now can be used to remove 'frotz/nitfol' to make room to fetch and
+ store 'frotz' remote-tracking branch.
+
+ * "diff.orderfile=<file>" configuration variable can be used to
+ pretend as if the "-O<file>" option were given from the command
+ line of "git diff", etc.
+
+ * The negative pathspec syntax allows "git log -- . ':!dir'" to tell
+ us "I am interested in everything but 'dir' directory".
+
+ * "git difftool" shows how many different paths there are in total,
+ and how many of them have been shown so far, to indicate progress.
+
+ * "git push origin master" used to push our 'master' branch to update
+ the 'master' branch at the 'origin' repository. This has been
+ enhanced to use the same ref mapping "git push origin" would use to
+ determine what ref at the 'origin' to be updated with our 'master'.
+ For example, with this configuration
+
+ [remote "origin"]
+ push = refs/heads/*:refs/review/*
+
+ that would cause "git push origin" to push out our local branches
+ to corresponding refs under refs/review/ hierarchy at 'origin',
+ "git push origin master" would update 'refs/review/master' over
+ there. Alternatively, if push.default is set to 'upstream' and our
+ 'master' is set to integrate with 'topic' from the 'origin' branch,
+ running "git push origin" while on our 'master' would update their
+ 'topic' branch, and running "git push origin master" while on any
+ of our branches does the same.
+
* "gitweb" learned to treat ref hierarchies other than refs/heads as
if they are additional branch namespaces (e.g. refs/changes/ in
Gerrit).
@@ -109,6 +154,16 @@ UI, Workflows & Features
Performance, Internal Implementation, etc.
+ * The naming convention of the packfiles has been updated; it used to
+ be based on the enumeration of names of the objects that are
+ contained in the pack, but now it also depends on how the packed
+ result is represented---packing the same set of objects using
+ different settings (or delta order) would produce a pack with
+ different name.
+
+ * "git diff --no-index" mode used to unnecessarily attempt to read
+ the index when there is one.
+
* The deprecated parse-options macro OPT_BOOLEAN has been removed;
use OPT_BOOL or OPT_COUNTUP in new code.
@@ -122,7 +177,8 @@ Performance, Internal Implementation, etc.
* "git merge-base" learned the "--fork-point" mode, that implements
the same logic used in "git pull --rebase" to find a suitable fork
point out of the reflog entries for the remote-tracking branch the
- work has been based on.
+ work has been based on. "git rebase" has the same logic that can be
+ triggered with the "--fork-point" option.
* A third-party "receive-pack" (the responder to "git push") can
advertise the "no-thin" capability to tell "git push" not to use
@@ -141,6 +197,63 @@ Unless otherwise noted, all the fixes since v1.8.5 in the maintenance
track are contained in this release (see the maintenance releases' notes
for details).
+ * The "--[no-]informative-errors" options to "git daemon" were parsed
+ a bit too loosely, allowing any other string after these option
+ names.
+ (merge 82246b7 nd/daemon-informative-errors-typofix later to maint).
+
+ * There is no reason to have a hardcoded upper limit of the number of
+ parents for an octopus merge, created via the graft mechanism, but
+ there was.
+ (merge e228c17 js/lift-parent-count-limit later to maint).
+
+ * The basic test used to leave unnecessary trash directories in the
+ t/ directory.
+ (merge 738a8be jk/test-framework-updates later to maint).
+
+ * "git merge-base --octopus" used to leave cleaning up suboptimal
+ result to the caller, but now it does the clean-up itself.
+ (merge 8f29299 bm/merge-base-octopus-dedup later to maint).
+
+ * A "gc" process running as a different user should be able to stop a
+ new "gc" process from starting, but it didn't.
+ (merge ed7eda8 km/gc-eperm later to maint).
+
+ * An earlier "clean-up" introduced an unnecessary memory leak.
+ (merge e1c1a32 jk/credential-plug-leak later to maint).
+
+ * "git add -A" (no other arguments) in a totally empty working tree
+ used to emit an error.
+ (merge 64ed07c nd/add-empty-fix later to maint).
+
+ * "git log --decorate" did not handle a tag pointed by another tag
+ nicely.
+ (merge 5e1361c bc/log-decoration later to maint).
+
+ * When we figure out how many file descriptors to allocate for
+ keeping packfiles open, a system with non-working getrlimit() could
+ cause us to die(), but because we make this call only to get a
+ rough estimate of how many is available and we do not even attempt
+ to use up all file descriptors available ourselves, it is nicer to
+ fall back to a reasonable low value rather than dying.
+ (merge 491a8de jh/rlimit-nofile-fallback later to maint).
+
+ * read_sha1_file(), that is the workhorse to read the contents given
+ an object name, honoured object replacements, but there was no
+ corresponding mechanism to sha1_object_info() that was used to
+ obtain the metainfo (e.g. type & size) about the object. This led
+ callers to weird inconsistencies.
+ (merge 663a856 cc/replace-object-info later to maint).
+
+ * "git cat-file --batch=", an admittedly useless command, did not
+ behave very well.
+ (merge 6554dfa jk/cat-file-regression-fix later to maint).
+
+ * "git rev-parse <revs> -- <paths>" did not implement the usual
+ disambiguation rules the commands in the "git log" family used in
+ the same way.
+ (merge 62f162f jk/rev-parse-double-dashes later to maint).
+
* "git mv A B/", when B does not exist as a directory, should error
out, but it didn't.
(merge c57f628 mm/mv-file-to-no-such-dir-with-slash later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index a4058063ce..ed59853190 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -567,6 +567,10 @@ be passed to the shell by Git, which will translate the final
command to `LESS=FRSX less -+S`. The environment tells the command
to set the `S` option to chop long lines but the command line
resets it to the default to fold long lines.
++
+Likewise, when the `LV` environment variable is unset, Git sets it
+to `-c`. You can override this setting by exporting `LV` with
+another value or setting `core.pager` to `lv +c`.
core.whitespace::
A comma separated list of common whitespace problems to
diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
index 223b9310df..f07b4513ed 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 bbed2cd79c..9b37b2a10b 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-cat-file.txt b/Documentation/git-cat-file.txt
index 322f5ed315..f6a16f4300 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -109,6 +109,11 @@ newline. The available atoms are:
The size, in bytes, that the object takes up on disk. See the
note about on-disk sizes in the `CAVEATS` section below.
+`deltabase`::
+ If the object is stored as a delta on-disk, this expands to the
+ 40-hex sha1 of the delta base object. Otherwise, expands to the
+ null sha1 (40 zeroes). See `CAVEATS` below.
+
`rest`::
If this atom is used in the output string, input lines are split
at the first whitespace boundary. All characters before that
@@ -152,10 +157,11 @@ should be taken in drawing conclusions about which refs or objects are
responsible for disk usage. The size of a packed non-delta object may be
much larger than the size of objects which delta against it, but the
choice of which object is the base and which is the delta is arbitrary
-and is subject to change during a repack. Note also that multiple copies
-of an object may be present in the object database; in this case, it is
-undefined which copy's size will be reported.
+and is subject to change during a repack.
+Note also that multiple copies of an object may be present in the object
+database; in this case, it is undefined which copy's size or delta base
+will be reported.
GIT
---
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index e4c8e82660..2eba627170 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-replace.txt b/Documentation/git-replace.txt
index f373ab48d4..0a02f70657 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/glossary-content.txt b/Documentation/glossary-content.txt
index aa1c8880dd..378306f581 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/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt
index f3c1357b7c..e3d6e7a79a 100644
--- a/Documentation/technical/api-builtin.txt
+++ b/Documentation/technical/api-builtin.txt
@@ -14,8 +14,8 @@ Git:
. Add the external declaration for the function to `builtin.h`.
-. Add the command to `commands[]` table in `handle_internal_command()`,
- defined in `git.c`. The entry should look like:
+. Add the command to the `commands[]` table defined in `git.c`.
+ The entry should look like:
{ "foo", cmd_foo, <options> },
+
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index cbb01a1ea2..248dcabd50 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1,5 +1,5 @@
Git User Manual
-_______________
+===============
Git is a fast distributed revision control system.
diff --git a/abspath.c b/abspath.c
index 8b3385a777..ca33558a91 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.h b/builtin.h
index d4afbfe418..c47c110e0f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -27,6 +27,8 @@ extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
+extern int is_builtin(const char *s);
+
extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_apply(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 226f758869..2a2722fa10 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/blame.c b/builtin/blame.c
index 4916eb2bd2..e44a6bb30a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1803,17 +1803,17 @@ static int prepare_lines(struct scoreboard *sb)
static int read_ancestry(const char *graft_file)
{
FILE *fp = fopen(graft_file, "r");
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
if (!fp)
return -1;
- while (fgets(buf, sizeof(buf), fp)) {
+ while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
- int len = strlen(buf);
- struct commit_graft *graft = read_graft_line(buf, len);
+ struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
if (graft)
register_commit_graft(graft, 0);
}
fclose(fp);
+ strbuf_release(&buf);
return 0;
}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index f8288c830c..d5a93e0e91 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -118,6 +118,7 @@ struct expand_data {
unsigned long size;
unsigned long disk_size;
const char *rest;
+ unsigned char delta_base_sha1[20];
/*
* If mark_query is true, we do not expand anything, but rather
@@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
data->split_on_whitespace = 1;
else if (data->rest)
strbuf_addstr(sb, data->rest);
+ } else if (is_atom("deltabase", atom, len)) {
+ if (data->mark_query)
+ data->info.delta_base_sha1 = data->delta_base_sha1;
+ else
+ strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1));
} else
die("unknown format element: %.*s", len, atom);
}
@@ -241,7 +247,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;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 1e7d617f46..09825c84d7 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/gc.c b/builtin/gc.c
index c14190f840..25f2237c08 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -222,7 +222,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
time(NULL) - st.st_mtime <= 12 * 3600 &&
fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
/* be gentle to concurrent "gc" on remote hosts */
- (strcmp(locking_host, my_host) || !kill(pid, 0));
+ (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
if (fp != NULL)
fclose(fp);
if (should_exit) {
diff --git a/builtin/help.c b/builtin/help.c
index cc17e670ce..1fdefeb686 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -288,6 +288,10 @@ static struct cmdnames main_cmds, other_cmds;
static int is_git_command(const char *s)
{
+ if (is_builtin(s))
+ return 1;
+
+ load_command_list("git-", &main_cmds, &other_cmds);
return is_in_cmdlist(&main_cmds, s) ||
is_in_cmdlist(&other_cmds, s);
}
@@ -449,7 +453,6 @@ int cmd_help(int argc, const char **argv, const char *prefix)
int nongit;
const char *alias;
enum help_format parsed_help_format;
- load_command_list("git-", &main_cmds, &other_cmds);
argc = parse_options(argc, argv, prefix, builtin_help_options,
builtin_help_usage, 0);
@@ -458,6 +461,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
if (show_all) {
git_config(git_help_config, NULL);
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+ load_command_list("git-", &main_cmds, &other_cmds);
list_commands(colopts, &main_cmds, &other_cmds);
}
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index a2923235e1..0ecde8da30 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -48,19 +48,36 @@ static struct commit *get_commit_reference(const char *arg)
return r;
}
-static int handle_octopus(int count, const char **args, int reduce, int show_all)
+static int handle_independent(int count, const char **args)
{
struct commit_list *revs = NULL;
struct commit_list *result;
int i;
- if (reduce)
- show_all = 1;
+ for (i = count - 1; i >= 0; i--)
+ commit_list_insert(get_commit_reference(args[i]), &revs);
+
+ result = reduce_heads(revs);
+ if (!result)
+ return 1;
+
+ while (result) {
+ printf("%s\n", sha1_to_hex(result->item->object.sha1));
+ result = result->next;
+ }
+ return 0;
+}
+
+static int handle_octopus(int count, const char **args, int show_all)
+{
+ struct commit_list *revs = NULL;
+ struct commit_list *result;
+ int i;
for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs);
- result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+ result = reduce_heads(get_octopus_merge_bases(revs));
if (!result)
return 1;
@@ -220,8 +237,11 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
if (cmdmode == 'r' && show_all)
die("--independent cannot be used with --all");
- if (cmdmode == 'r' || cmdmode == 'o')
- return handle_octopus(argc, argv, cmdmode == 'r', show_all);
+ if (cmdmode == 'o')
+ return handle_octopus(argc, argv, show_all);
+
+ if (cmdmode == 'r')
+ return handle_independent(argc, argv);
if (cmdmode == 'f') {
if (argc < 1 || 2 < argc)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index dfb4d84caa..541667f102 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -737,8 +737,6 @@ static void write_pack_file(void)
f = create_tmp_packfile(&pack_tmp_name);
offset = write_pack_header(f, nr_remaining);
- if (!offset)
- die_errno("unable to write pack header");
nr_written = 0;
for (; i < nr_objects; i++) {
struct object_entry *e = write_order[i];
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index fa6ce42f44..fcf5fb6129 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 a85ef54d5a..beb0dc942c 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/replace.c b/builtin/replace.c
index b1bd3ef994..2336325ce3 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/cache.h b/cache.h
index ce377e1354..83a27269b8 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 *);
@@ -1074,6 +1080,7 @@ struct object_info {
enum object_type *typep;
unsigned long *sizep;
unsigned long *disk_sizep;
+ unsigned char *delta_base_sha1;
/* Response */
enum {
@@ -1098,7 +1105,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/commit.c b/commit.c
index 5df1df7351..6bf4fe00d4 100644
--- a/commit.c
+++ b/commit.c
@@ -196,19 +196,19 @@ bad_graft_data:
static int read_graft_file(const char *graft_file)
{
FILE *fp = fopen(graft_file, "r");
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
if (!fp)
return -1;
- while (fgets(buf, sizeof(buf), fp)) {
+ while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
- int len = strlen(buf);
- struct commit_graft *graft = read_graft_line(buf, len);
+ struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
if (!graft)
continue;
if (register_commit_graft(graft, 1))
- error("duplicate graft data: %s", buf);
+ error("duplicate graft data: %s", buf.buf);
}
fclose(fp);
+ strbuf_release(&buf);
return 0;
}
@@ -841,26 +841,26 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
struct commit_list *get_octopus_merge_bases(struct commit_list *in)
{
struct commit_list *i, *j, *k, *ret = NULL;
- struct commit_list **pptr = &ret;
- for (i = in; i; i = i->next) {
- if (!ret)
- pptr = &commit_list_insert(i->item, pptr)->next;
- else {
- struct commit_list *new = NULL, *end = NULL;
-
- for (j = ret; j; j = j->next) {
- struct commit_list *bases;
- bases = get_merge_bases(i->item, j->item, 1);
- if (!new)
- new = bases;
- else
- end->next = bases;
- for (k = bases; k; k = k->next)
- end = k;
- }
- ret = new;
+ if (!in)
+ return ret;
+
+ commit_list_insert(in->item, &ret);
+
+ for (i = in->next; i; i = i->next) {
+ struct commit_list *new = NULL, *end = NULL;
+
+ for (j = ret; j; j = j->next) {
+ struct commit_list *bases;
+ bases = get_merge_bases(i->item, j->item, 1);
+ if (!new)
+ new = bases;
+ else
+ end->next = bases;
+ for (k = bases; k; k = k->next)
+ end = k;
}
+ ret = new;
}
return ret;
}
@@ -1356,7 +1356,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
}
-int commit_tree(const struct strbuf *msg, unsigned char *tree,
+int commit_tree(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit)
{
@@ -1485,7 +1485,7 @@ static const char commit_utf8_warn[] =
"You may want to amend it after fixing the message, or set the config\n"
"variable i18n.commitencoding to the encoding your project uses.\n";
-int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *extra)
diff --git a/commit.h b/commit.h
index 934af889f7..f8a451d864 100644
--- a/commit.h
+++ b/commit.h
@@ -232,11 +232,11 @@ struct commit_extra_header {
extern void append_merge_tag_headers(struct commit_list *parents,
struct commit_extra_header ***tail);
-extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit);
-extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *);
diff --git a/config.mak.uname b/config.mak.uname
index 82d549e48b..7d31fad241 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -198,6 +198,13 @@ ifeq ($(uname_S),OpenBSD)
BASIC_LDFLAGS += -L/usr/local/lib
HAVE_PATHS_H = YesPlease
endif
+ifeq ($(uname_S),MirBSD)
+ NO_STRCASESTR = YesPlease
+ NO_MEMMEM = YesPlease
+ USE_ST_TIMESPEC = YesPlease
+ NEEDS_LIBICONV = YesPlease
+ HAVE_PATHS_H = YesPlease
+endif
ifeq ($(uname_S),NetBSD)
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
NEEDS_LIBICONV = YesPlease
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 51c2dd4dec..8aaf214b7d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -178,9 +178,9 @@ _get_comp_words_by_ref ()
}
fi
-__gitcompadd ()
+__gitcompappend ()
{
- local i=0
+ local i=${#COMPREPLY[@]}
for x in $1; do
if [[ "$x" == "$3"* ]]; then
COMPREPLY[i++]="$2$x$4"
@@ -188,6 +188,12 @@ __gitcompadd ()
done
}
+__gitcompadd ()
+{
+ COMPREPLY=()
+ __gitcompappend "$@"
+}
+
# Generates completion reply, appending a space to possible completion words,
# if necessary.
# It accepts 1 to 4 arguments:
@@ -218,6 +224,14 @@ __gitcomp ()
esac
}
+# Variation of __gitcomp_nl () that appends to the existing list of
+# completion candidates, COMPREPLY.
+__gitcomp_nl_append ()
+{
+ local IFS=$'\n'
+ __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
+}
+
# Generates completion reply from newline-separated possible completion words
# by appending a space to all of them.
# It accepts 1 to 4 arguments:
@@ -229,8 +243,8 @@ __gitcomp ()
# appended.
__gitcomp_nl ()
{
- local IFS=$'\n'
- __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
+ COMPREPLY=()
+ __gitcomp_nl_append "$@"
}
# Generates completion reply with compgen from newline-separated possible
@@ -1827,6 +1841,7 @@ _git_config ()
branch.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+ __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
return
;;
guitool.*.*)
@@ -1869,6 +1884,7 @@ _git_config ()
remote.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+ __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
return
;;
url.*.*)
@@ -1991,6 +2007,7 @@ _git_config ()
fetch.unpackLimit
format.attach
format.cc
+ format.coverLetter
format.headers
format.numbered
format.pretty
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index fac5e711eb..6b77968572 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -30,10 +30,10 @@ if [ -z "$script" ]; then
local -a locations
local e
locations=(
+ $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
'/etc/bash_completion.d/git' # fedora, old debian
'/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
'/usr/share/bash-completion/git' # gentoo
- $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
)
for e in $locations; do
test -f $e && script="$e" && break
@@ -76,6 +76,14 @@ __gitcomp_nl ()
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
}
+__gitcomp_nl_append ()
+{
+ emulate -L zsh
+
+ local IFS=$'\n'
+ compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
+}
+
__gitcomp_file ()
{
emulate -L zsh
diff --git a/csum-file.c b/csum-file.c
index 465971c7f3..b00b215031 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -86,7 +86,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
return fd;
}
-int sha1write(struct sha1file *f, const void *buf, unsigned int count)
+void sha1write(struct sha1file *f, const void *buf, unsigned int count)
{
while (count) {
unsigned offset = f->offset;
@@ -116,7 +116,6 @@ int sha1write(struct sha1file *f, const void *buf, unsigned int count)
}
f->offset = offset;
}
- return 0;
}
struct sha1file *sha1fd(int fd, const char *name)
diff --git a/csum-file.h b/csum-file.h
index 9dedb038ea..bb543d52f1 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 int sha1write(struct sha1file *, const 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 7bee953de8..503e03906c 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.c b/diff.c
index f99bc83b73..6b4cd0eb36 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);
@@ -3219,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) {
diff --git a/diffcore-order.c b/diffcore-order.c
index 23e93852d8..fe7f1f4647 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 23b6de4703..d10a63f731 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-compat-util.h b/git-compat-util.h
index b73916bbc6..cbd86c37f5 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -75,7 +75,7 @@
# endif
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
!defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
- !defined(__TANDEM) && !defined(__QNX__)
+ !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
diff --git a/git-rebase.sh b/git-rebase.sh
index 7185dc8438..8a3efa2983 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -534,7 +534,8 @@ esac
if test "$fork_point" = t
then
- new_upstream=$(git merge-base --fork-point "$upstream_name" "$switch_to")
+ new_upstream=$(git merge-base --fork-point "$upstream_name" \
+ "${switch_to:-HEAD}")
if test -n "$new_upstream"
then
upstream=$new_upstream
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 190a5394b9..fffa3c72d7 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -159,7 +159,8 @@ git_pager() {
GIT_PAGER=cat
fi
: ${LESS=-FRSX}
- export LESS
+ : ${LV=-c}
+ export LESS LV
eval "$GIT_PAGER" '"$@"'
}
diff --git a/git-stash.sh b/git-stash.sh
index 1e541a2125..f0a94abf14 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -358,7 +358,7 @@ parse_flags_and_rev()
i_tree=
u_tree=
- REV=$(git rev-parse --no-flags --symbolic "$@") || exit 1
+ REV=$(git rev-parse --no-flags --symbolic --sq "$@") || exit 1
FLAGS=
for opt
@@ -376,7 +376,7 @@ parse_flags_and_rev()
esac
done
- set -- $REV
+ eval set -- $REV
case $# in
0)
@@ -391,13 +391,13 @@ parse_flags_and_rev()
;;
esac
- REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || {
+ REV=$(git rev-parse --quiet --symbolic --verify "$1" 2>/dev/null) || {
reference="$1"
die "$(eval_gettext "\$reference is not valid reference")"
}
- i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
- set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
+ i_commit=$(git rev-parse --quiet --verify "$REV^2" 2>/dev/null) &&
+ set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
s=$1 &&
w_commit=$1 &&
b_commit=$2 &&
@@ -408,8 +408,8 @@ parse_flags_and_rev()
test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
IS_STASH_REF=t
- u_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&
- u_tree=$(git rev-parse $REV^3: 2>/dev/null)
+ u_commit=$(git rev-parse --quiet --verify "$REV^3" 2>/dev/null) &&
+ u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
}
is_stash_like()
diff --git a/git.c b/git.c
index 3799514ccb..bba4378458 100644
--- a/git.c
+++ b/git.c
@@ -332,124 +332,136 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
return 0;
}
-static void handle_internal_command(int argc, const char **argv)
+static struct cmd_struct commands[] = {
+ { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ { "annotate", cmd_annotate, RUN_SETUP },
+ { "apply", cmd_apply, RUN_SETUP_GENTLY },
+ { "archive", cmd_archive },
+ { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+ { "blame", cmd_blame, RUN_SETUP },
+ { "branch", cmd_branch, RUN_SETUP },
+ { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
+ { "cat-file", cmd_cat_file, RUN_SETUP },
+ { "check-attr", cmd_check_attr, RUN_SETUP },
+ { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
+ { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
+ { "check-ref-format", cmd_check_ref_format },
+ { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
+ { "checkout-index", cmd_checkout_index,
+ RUN_SETUP | NEED_WORK_TREE},
+ { "cherry", cmd_cherry, RUN_SETUP },
+ { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+ { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
+ { "clone", cmd_clone },
+ { "column", cmd_column, RUN_SETUP_GENTLY },
+ { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
+ { "commit-tree", cmd_commit_tree, RUN_SETUP },
+ { "config", cmd_config, RUN_SETUP_GENTLY },
+ { "count-objects", cmd_count_objects, RUN_SETUP },
+ { "credential", cmd_credential, RUN_SETUP_GENTLY },
+ { "describe", cmd_describe, RUN_SETUP },
+ { "diff", cmd_diff },
+ { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
+ { "diff-index", cmd_diff_index, RUN_SETUP },
+ { "diff-tree", cmd_diff_tree, RUN_SETUP },
+ { "fast-export", cmd_fast_export, RUN_SETUP },
+ { "fetch", cmd_fetch, RUN_SETUP },
+ { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
+ { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+ { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
+ { "format-patch", cmd_format_patch, RUN_SETUP },
+ { "fsck", cmd_fsck, RUN_SETUP },
+ { "fsck-objects", cmd_fsck, RUN_SETUP },
+ { "gc", cmd_gc, RUN_SETUP },
+ { "get-tar-commit-id", cmd_get_tar_commit_id },
+ { "grep", cmd_grep, RUN_SETUP_GENTLY },
+ { "hash-object", cmd_hash_object },
+ { "help", cmd_help },
+ { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
+ { "init", cmd_init_db },
+ { "init-db", cmd_init_db },
+ { "log", cmd_log, RUN_SETUP },
+ { "ls-files", cmd_ls_files, RUN_SETUP },
+ { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
+ { "ls-tree", cmd_ls_tree, RUN_SETUP },
+ { "mailinfo", cmd_mailinfo },
+ { "mailsplit", cmd_mailsplit },
+ { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-base", cmd_merge_base, RUN_SETUP },
+ { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
+ { "merge-index", cmd_merge_index, RUN_SETUP },
+ { "merge-ours", cmd_merge_ours, RUN_SETUP },
+ { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-tree", cmd_merge_tree, RUN_SETUP },
+ { "mktag", cmd_mktag, RUN_SETUP },
+ { "mktree", cmd_mktree, RUN_SETUP },
+ { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
+ { "name-rev", cmd_name_rev, RUN_SETUP },
+ { "notes", cmd_notes, RUN_SETUP },
+ { "pack-objects", cmd_pack_objects, RUN_SETUP },
+ { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
+ { "pack-refs", cmd_pack_refs, RUN_SETUP },
+ { "patch-id", cmd_patch_id },
+ { "pickaxe", cmd_blame, RUN_SETUP },
+ { "prune", cmd_prune, RUN_SETUP },
+ { "prune-packed", cmd_prune_packed, RUN_SETUP },
+ { "push", cmd_push, RUN_SETUP },
+ { "read-tree", cmd_read_tree, RUN_SETUP },
+ { "receive-pack", cmd_receive_pack },
+ { "reflog", cmd_reflog, RUN_SETUP },
+ { "remote", cmd_remote, RUN_SETUP },
+ { "remote-ext", cmd_remote_ext },
+ { "remote-fd", cmd_remote_fd },
+ { "repack", cmd_repack, RUN_SETUP },
+ { "replace", cmd_replace, RUN_SETUP },
+ { "rerere", cmd_rerere, RUN_SETUP },
+ { "reset", cmd_reset, RUN_SETUP },
+ { "rev-list", cmd_rev_list, RUN_SETUP },
+ { "rev-parse", cmd_rev_parse },
+ { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+ { "rm", cmd_rm, RUN_SETUP },
+ { "send-pack", cmd_send_pack, RUN_SETUP },
+ { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
+ { "show", cmd_show, RUN_SETUP },
+ { "show-branch", cmd_show_branch, RUN_SETUP },
+ { "show-ref", cmd_show_ref, RUN_SETUP },
+ { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
+ { "stripspace", cmd_stripspace },
+ { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
+ { "tag", cmd_tag, RUN_SETUP },
+ { "unpack-file", cmd_unpack_file, RUN_SETUP },
+ { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
+ { "update-index", cmd_update_index, RUN_SETUP },
+ { "update-ref", cmd_update_ref, RUN_SETUP },
+ { "update-server-info", cmd_update_server_info, RUN_SETUP },
+ { "upload-archive", cmd_upload_archive },
+ { "upload-archive--writer", cmd_upload_archive_writer },
+ { "var", cmd_var, RUN_SETUP_GENTLY },
+ { "verify-pack", cmd_verify_pack },
+ { "verify-tag", cmd_verify_tag, RUN_SETUP },
+ { "version", cmd_version },
+ { "whatchanged", cmd_whatchanged, RUN_SETUP },
+ { "write-tree", cmd_write_tree, RUN_SETUP },
+};
+
+int is_builtin(const char *s)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ struct cmd_struct *p = commands+i;
+ if (!strcmp(s, p->cmd))
+ return 1;
+ }
+ return 0;
+}
+
+static void handle_builtin(int argc, const char **argv)
{
const char *cmd = argv[0];
- static struct cmd_struct commands[] = {
- { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
- { "annotate", cmd_annotate, RUN_SETUP },
- { "apply", cmd_apply, RUN_SETUP_GENTLY },
- { "archive", cmd_archive },
- { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
- { "blame", cmd_blame, RUN_SETUP },
- { "branch", cmd_branch, RUN_SETUP },
- { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
- { "cat-file", cmd_cat_file, RUN_SETUP },
- { "check-attr", cmd_check_attr, RUN_SETUP },
- { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
- { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
- { "check-ref-format", cmd_check_ref_format },
- { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
- { "checkout-index", cmd_checkout_index,
- RUN_SETUP | NEED_WORK_TREE},
- { "cherry", cmd_cherry, RUN_SETUP },
- { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
- { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
- { "clone", cmd_clone },
- { "column", cmd_column, RUN_SETUP_GENTLY },
- { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
- { "commit-tree", cmd_commit_tree, RUN_SETUP },
- { "config", cmd_config, RUN_SETUP_GENTLY },
- { "count-objects", cmd_count_objects, RUN_SETUP },
- { "credential", cmd_credential, RUN_SETUP_GENTLY },
- { "describe", cmd_describe, RUN_SETUP },
- { "diff", cmd_diff },
- { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
- { "diff-index", cmd_diff_index, RUN_SETUP },
- { "diff-tree", cmd_diff_tree, RUN_SETUP },
- { "fast-export", cmd_fast_export, RUN_SETUP },
- { "fetch", cmd_fetch, RUN_SETUP },
- { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
- { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
- { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
- { "format-patch", cmd_format_patch, RUN_SETUP },
- { "fsck", cmd_fsck, RUN_SETUP },
- { "fsck-objects", cmd_fsck, RUN_SETUP },
- { "gc", cmd_gc, RUN_SETUP },
- { "get-tar-commit-id", cmd_get_tar_commit_id },
- { "grep", cmd_grep, RUN_SETUP_GENTLY },
- { "hash-object", cmd_hash_object },
- { "help", cmd_help },
- { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
- { "init", cmd_init_db },
- { "init-db", cmd_init_db },
- { "log", cmd_log, RUN_SETUP },
- { "ls-files", cmd_ls_files, RUN_SETUP },
- { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
- { "ls-tree", cmd_ls_tree, RUN_SETUP },
- { "mailinfo", cmd_mailinfo },
- { "mailsplit", cmd_mailsplit },
- { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
- { "merge-base", cmd_merge_base, RUN_SETUP },
- { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
- { "merge-index", cmd_merge_index, RUN_SETUP },
- { "merge-ours", cmd_merge_ours, RUN_SETUP },
- { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
- { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
- { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
- { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
- { "merge-tree", cmd_merge_tree, RUN_SETUP },
- { "mktag", cmd_mktag, RUN_SETUP },
- { "mktree", cmd_mktree, RUN_SETUP },
- { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
- { "name-rev", cmd_name_rev, RUN_SETUP },
- { "notes", cmd_notes, RUN_SETUP },
- { "pack-objects", cmd_pack_objects, RUN_SETUP },
- { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
- { "pack-refs", cmd_pack_refs, RUN_SETUP },
- { "patch-id", cmd_patch_id },
- { "pickaxe", cmd_blame, RUN_SETUP },
- { "prune", cmd_prune, RUN_SETUP },
- { "prune-packed", cmd_prune_packed, RUN_SETUP },
- { "push", cmd_push, RUN_SETUP },
- { "read-tree", cmd_read_tree, RUN_SETUP },
- { "receive-pack", cmd_receive_pack },
- { "reflog", cmd_reflog, RUN_SETUP },
- { "remote", cmd_remote, RUN_SETUP },
- { "remote-ext", cmd_remote_ext },
- { "remote-fd", cmd_remote_fd },
- { "repack", cmd_repack, RUN_SETUP },
- { "replace", cmd_replace, RUN_SETUP },
- { "rerere", cmd_rerere, RUN_SETUP },
- { "reset", cmd_reset, RUN_SETUP },
- { "rev-list", cmd_rev_list, RUN_SETUP },
- { "rev-parse", cmd_rev_parse },
- { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
- { "rm", cmd_rm, RUN_SETUP },
- { "send-pack", cmd_send_pack, RUN_SETUP },
- { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
- { "show", cmd_show, RUN_SETUP },
- { "show-branch", cmd_show_branch, RUN_SETUP },
- { "show-ref", cmd_show_ref, RUN_SETUP },
- { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
- { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
- { "stripspace", cmd_stripspace },
- { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
- { "tag", cmd_tag, RUN_SETUP },
- { "unpack-file", cmd_unpack_file, RUN_SETUP },
- { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
- { "update-index", cmd_update_index, RUN_SETUP },
- { "update-ref", cmd_update_ref, RUN_SETUP },
- { "update-server-info", cmd_update_server_info, RUN_SETUP },
- { "upload-archive", cmd_upload_archive },
- { "upload-archive--writer", cmd_upload_archive_writer },
- { "var", cmd_var, RUN_SETUP_GENTLY },
- { "verify-pack", cmd_verify_pack },
- { "verify-tag", cmd_verify_tag, RUN_SETUP },
- { "version", cmd_version },
- { "whatchanged", cmd_whatchanged, RUN_SETUP },
- { "write-tree", cmd_write_tree, RUN_SETUP },
- };
int i;
static const char ext[] = STRIP_EXTENSION;
@@ -517,8 +529,8 @@ static int run_argv(int *argcp, const char ***argv)
int done_alias = 0;
while (1) {
- /* See if it's an internal command */
- handle_internal_command(*argcp, *argv);
+ /* See if it's a builtin */
+ handle_builtin(*argcp, *argv);
/* .. then try the external ones */
execv_dashed_external(*argv);
@@ -563,14 +575,14 @@ int main(int argc, char **av)
* - cannot execute it externally (since it would just do
* the same thing over again)
*
- * So we just directly call the internal command handler, and
- * die if that one cannot handle it.
+ * So we just directly call the builtin handler, and die if
+ * that one cannot handle it.
*/
if (starts_with(cmd, "git-")) {
cmd += 4;
argv[0] = cmd;
- handle_internal_command(argc, argv);
- die("cannot handle %s internally", cmd);
+ handle_builtin(argc, argv);
+ die("cannot handle %s as a builtin", cmd);
}
/* Look for flags.. */
diff --git a/log-tree.c b/log-tree.c
index 642faffdb7..08970bf46e 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 ddc174e1ad..605d01b25c 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -183,8 +183,7 @@ off_t write_pack_header(struct sha1file *f, uint32_t nr_entries)
hdr.hdr_signature = htonl(PACK_SIGNATURE);
hdr.hdr_version = htonl(PACK_VERSION);
hdr.hdr_entries = htonl(nr_entries);
- if (sha1write(f, &hdr, sizeof(hdr)))
- return 0;
+ sha1write(f, &hdr, sizeof(hdr));
return sizeof(hdr);
}
diff --git a/pager.c b/pager.c
index 345b0bc4b5..0cc75a8eee 100644
--- a/pager.c
+++ b/pager.c
@@ -80,8 +80,15 @@ void setup_pager(void)
pager_process.use_shell = 1;
pager_process.argv = pager_argv;
pager_process.in = -1;
- if (!getenv("LESS")) {
- static const char *env[] = { "LESS=FRSX", NULL };
+ if (!getenv("LESS") || !getenv("LV")) {
+ static const char *env[3];
+ int i = 0;
+
+ if (!getenv("LESS"))
+ env[i++] = "LESS=FRSX";
+ if (!getenv("LV"))
+ env[i++] = "LV=-c";
+ env[i] = NULL;
pager_process.env = env;
}
if (start_command(&pager_process))
diff --git a/pathspec.c b/pathspec.c
index 52d38a4b4f..6cb9fd3014 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 a75e9242d1..0c1126264a 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/Log.pm b/perl/Git/SVN/Log.pm
index 3f8350a57d..34f2869ab5 100644
--- a/perl/Git/SVN/Log.pm
+++ b/perl/Git/SVN/Log.pm
@@ -117,6 +117,7 @@ sub run_pager {
}
open STDIN, '<&', $rfd or fatal "Can't redirect stdin: $!";
$ENV{LESS} ||= 'FRSX';
+ $ENV{LV} ||= '-c';
exec $pager or fatal "Can't run pager: $! ($pager)";
}
diff --git a/prompt.c b/prompt.c
index d851807feb..d7bb17cb66 100644
--- a/prompt.c
+++ b/prompt.c
@@ -22,6 +22,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
if (start_command(&pass))
return NULL;
+ strbuf_reset(&buffer);
if (strbuf_read(&buffer, pass.out, 20) < 0)
err = 1;
@@ -38,7 +39,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
- return strbuf_detach(&buffer, NULL);
+ return buffer.buf;
}
char *git_prompt(const char *prompt, int flags)
diff --git a/replace_object.c b/replace_object.c
index d0b1548726..cdcaf8cbe2 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 daacc0cfb0..e13bd2c3ee 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
+
+#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
- return lim.rlim_cur;
-#elif defined(_SC_OPEN_MAX)
- return sysconf(_SC_OPEN_MAX);
-#elif defined(OPEN_MAX)
+#ifdef OPEN_MAX
return OPEN_MAX;
#else
return 1; /* see the caller ;-) */
@@ -1667,6 +1690,38 @@ static off_t get_delta_base(struct packed_git *p,
return base_offset;
}
+/*
+ * Like get_delta_base above, but we return the sha1 instead of the pack
+ * offset. This means it is cheaper for REF deltas (we do not have to do
+ * the final object lookup), but more expensive for OFS deltas (we
+ * have to load the revidx to convert the offset back into a sha1).
+ */
+static const unsigned char *get_delta_base_sha1(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos,
+ enum object_type type,
+ off_t delta_obj_offset)
+{
+ if (type == OBJ_REF_DELTA) {
+ unsigned char *base = use_pack(p, w_curs, curpos, NULL);
+ return base;
+ } else if (type == OBJ_OFS_DELTA) {
+ struct revindex_entry *revidx;
+ off_t base_offset = get_delta_base(p, w_curs, &curpos,
+ type, delta_obj_offset);
+
+ if (!base_offset)
+ return NULL;
+
+ revidx = find_pack_revindex(p, base_offset);
+ if (!revidx)
+ return NULL;
+
+ return nth_packed_object_sha1(p, revidx->nr);
+ } else
+ return NULL;
+}
+
int unpack_object_header(struct packed_git *p,
struct pack_window **w_curs,
off_t *curpos,
@@ -1824,6 +1879,22 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
}
}
+ if (oi->delta_base_sha1) {
+ if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+ const unsigned char *base;
+
+ base = get_delta_base_sha1(p, &w_curs, curpos,
+ type, obj_offset);
+ if (!base) {
+ type = OBJ_BAD;
+ goto out;
+ }
+
+ hashcpy(oi->delta_base_sha1, base);
+ } else
+ hashclr(oi->delta_base_sha1);
+ }
+
out:
unuse_pack(&w_curs);
return type;
@@ -2407,6 +2478,9 @@ static int sha1_loose_object_info(const unsigned char *sha1,
git_zstream stream;
char hdr[32];
+ if (oi->delta_base_sha1)
+ hashclr(oi->delta_base_sha1);
+
/*
* If we don't care about type or size, then we don't
* need to look inside the object at all. Note that we
@@ -2443,13 +2517,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;
@@ -2457,27 +2532,29 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
*(oi->sizep) = co->size;
if (oi->disk_sizep)
*(oi->disk_sizep) = 0;
+ if (oi->delta_base_sha1)
+ hashclr(oi->delta_base_sha1);
oi->whence = OI_CACHED;
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 +2576,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 +2668,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/sha1_name.c b/sha1_name.c
index b1873d8113..a5578f718e 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -451,9 +451,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
int at, reflog_len, nth_prior = 0;
if (len == 40 && !get_sha1_hex(str, sha1)) {
- if (warn_on_object_refname_ambiguity) {
+ if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
- if (refs_found > 0 && warn_ambiguous_refs) {
+ if (refs_found > 0) {
warning(warn_msg, len, str);
if (advice_object_name_warning)
fprintf(stderr, "%s\n", _(object_name_msg));
diff --git a/streaming.c b/streaming.c
index debe904523..9659f18be2 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/annotate-tests.sh b/t/annotate-tests.sh
index c9d105d707..304c7b7d87 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -116,6 +116,27 @@ test_expect_success 'blame evil merge' '
check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
'
+test_expect_success 'blame huge graft' '
+ test_when_finished "git checkout branch2" &&
+ test_when_finished "rm -f .git/info/grafts" &&
+ graft= &&
+ for i in 0 1 2
+ do
+ for j in 0 1 2 3 4 5 6 7 8 9
+ do
+ git checkout --orphan "$i$j" &&
+ printf "%s\n" "$i" "$j" >file &&
+ test_tick &&
+ GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
+ git commit -a -m "$i$j" &&
+ commit=$(git rev-parse --verify HEAD) &&
+ graft="$graft$commit "
+ done
+ done &&
+ printf "%s " $graft >.git/info/grafts &&
+ check_count -h 00 01 1 10 1
+'
+
test_expect_success 'setup incomplete line' '
echo "incomplete" | tr -d "\\012" >>file &&
GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index c4707843c4..bfdff2a8c9 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -129,7 +129,7 @@ prepare_httpd() {
HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT
HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST
HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
- HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST
+ HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST
if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
then
@@ -217,7 +217,15 @@ setup_askpass_helper() {
test_expect_success 'setup askpass helper' '
write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
- cat "$TRASH_DIRECTORY/askpass-response"
+ case "$*" in
+ *Username*)
+ what=user
+ ;;
+ *Password*)
+ what=pass
+ ;;
+ esac &&
+ cat "$TRASH_DIRECTORY/askpass-$what"
EOF
GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
export GIT_ASKPASS &&
@@ -227,7 +235,8 @@ setup_askpass_helper() {
set_askpass() {
>"$TRASH_DIRECTORY/askpass-query" &&
- echo "$*" >"$TRASH_DIRECTORY/askpass-response"
+ echo "$1" >"$TRASH_DIRECTORY/askpass-user" &&
+ echo "$2" >"$TRASH_DIRECTORY/askpass-pass"
}
expect_askpass() {
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
index f2fbcad33e..99a34d6487 100644
--- a/t/lib-httpd/passwd
+++ b/t/lib-httpd/passwd
@@ -1 +1 @@
-user@host:nKpa8pZUHx/ic
+user@host:xb4E8pqD81KQs
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 10be52beed..a2bb63ce8e 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -41,20 +41,17 @@ test_expect_success '.git/objects should have 3 subdirectories' '
test_expect_success 'success is reported like this' '
:
'
-test_expect_failure 'pretend we have a known breakage' '
- false
-'
run_sub_test_lib_test () {
name="$1" descr="$2" # stdin is the body of the test code
shift 2
mkdir "$name" &&
(
- # Pretend we're a test harness. This prevents
- # test-lib from writing the counts to a file that will
- # later be summarized, showing spurious "failed" tests
- HARNESS_ACTIVE=t &&
- export HARNESS_ACTIVE &&
+ # Pretend we're not running under a test harness, whether we
+ # are or not. The test-lib output depends on the setting of
+ # this variable, so we need a stable setting under which to run
+ # the sub-test.
+ sane_unset HARNESS_ACTIVE &&
cd "$name" &&
cat >"$name.sh" <<-EOF &&
#!$SHELL_PATH
@@ -71,6 +68,8 @@ run_sub_test_lib_test () {
cat >>"$name.sh" &&
chmod +x "$name.sh" &&
export TEST_DIRECTORY &&
+ TEST_OUTPUT_DIRECTORY=$(pwd) &&
+ export TEST_OUTPUT_DIRECTORY &&
./"$name.sh" "$@" >out 2>err
)
}
@@ -233,16 +232,13 @@ test_expect_success 'test --verbose' '
grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
check_sub_test_lib_test test-verbose <<-\EOF
> expecting success: true
- > Z
> ok 1 - passing test
> Z
> expecting success: echo foo
> foo
- > Z
> ok 2 - test with output
> Z
> expecting success: false
- > Z
> not ok 3 - failing test
> # false
> Z
@@ -265,7 +261,6 @@ test_expect_success 'test --verbose-only' '
> Z
> expecting success: echo foo
> foo
- > Z
> ok 2 - test with output
> Z
> not ok 3 - failing test
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 1687098355..a72e700ae4 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -262,4 +262,38 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
"$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
'
+test_expect_success 'setup blobs which are likely to delta' '
+ test-genrandom foo 10240 >foo &&
+ { cat foo; echo plus; } >foo-plus &&
+ git add foo foo-plus &&
+ git commit -m foo &&
+ cat >blobs <<-\EOF
+ HEAD:foo
+ HEAD:foo-plus
+ EOF
+'
+
+test_expect_success 'confirm that neither loose blob is a delta' '
+ cat >expect <<-EOF
+ $_z40
+ $_z40
+ EOF
+ git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+ test_cmp expect actual
+'
+
+# To avoid relying too much on the current delta heuristics,
+# we will check only that one of the two objects is a delta
+# against the other, but not the order. We can do so by just
+# asking for the base of both, and checking whether either
+# sha1 appears in the output.
+test_expect_success '%(deltabase) reports packed delta bases' '
+ git repack -ad &&
+ git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+ {
+ grep "$(git rev-parse HEAD:foo)" actual ||
+ grep "$(git rev-parse HEAD:foo-plus)" actual
+ }
+'
+
test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 998503db12..6d94b1fcd9 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -135,11 +135,19 @@ test_expect_success 'fail when upstream arg is missing and not configured' '
'
test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -b default-base master &&
git checkout -b default topic &&
git config branch.default.remote . &&
- git config branch.default.merge refs/heads/master &&
+ git config branch.default.merge refs/heads/default-base &&
git rebase &&
- git rev-parse --verify master >expect &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase &&
+ git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual
'
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index aab86e838b..fe274e2fb1 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/t3903-stash.sh b/t/t3903-stash.sh
index debda7a678..5b79b216e2 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -673,4 +673,16 @@ test_expect_success 'store updates stash ref and reflog' '
grep quux bazzy
'
+test_expect_success 'handle stash specification with spaces' '
+ git stash clear &&
+ echo pig >file &&
+ git stash &&
+ stamp=$(git log -g --format="%cd" -1 refs/stash) &&
+ test_tick &&
+ echo cow >file &&
+ git stash &&
+ git stash apply "stash@{$stamp}" &&
+ grep pig file
+'
+
test_done
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
new file mode 100755
index 0000000000..1ddd226b78
--- /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 fb00041139..2a6278bb33 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/t5510-fetch.sh b/t/t5510-fetch.sh
index 5d4581dac8..12674ac098 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/t5540-http-push.sh b/t/t5540-http-push.sh
index 01d0d95b4d..5b0198cbc8 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -154,7 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
test_expect_success 'push to password-protected repository (user in URL)' '
test_commit pw-user &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
git rev-parse --verify HEAD >expect &&
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
@@ -168,7 +168,7 @@ test_expect_failure 'user was prompted only once for password' '
test_expect_failure 'push to password-protected repository (no user in URL)' '
test_commit pw-nouser &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
expect_askpass both user@host
git rev-parse --verify HEAD >expect &&
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
index 470ac54295..bfd241ea8a 100755
--- a/t/t5541-http-push.sh
+++ b/t/t5541-http-push.sh
@@ -274,7 +274,7 @@ test_expect_success 'push over smart http with auth' '
cd "$ROOT_PATH/test_repo_clone" &&
echo push-auth-test >expect &&
test_commit push-auth-test &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git push "$HTTPD_URL"/auth/smart/test_repo.git &&
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
log -1 --format=%s >actual &&
@@ -286,7 +286,7 @@ test_expect_success 'push to auth-only-for-push repo' '
cd "$ROOT_PATH/test_repo_clone" &&
echo push-half-auth >expect &&
test_commit push-half-auth &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
log -1 --format=%s >actual &&
@@ -316,7 +316,7 @@ test_expect_success 'push into half-auth-complete requires password' '
cd "$ROOT_PATH/half-auth-clone" &&
echo two >expect &&
test_commit two &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
log -1 --format=%s >actual &&
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index f7d0f146f0..8392624714 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -62,13 +62,13 @@ test_expect_success 'http auth can use user/pass in URL' '
'
test_expect_success 'http auth can use just user in URL' '
- set_askpass user@host &&
+ set_askpass wrong pass@host &&
git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
expect_askpass pass user@host
'
test_expect_success 'http auth can request both user and pass' '
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
expect_askpass both user@host
'
@@ -77,7 +77,7 @@ test_expect_success 'http auth respects credential helper config' '
test_config_global credential.helper "!f() {
cat >/dev/null
echo username=user@host
- echo password=user@host
+ echo password=pass@host
}; f" &&
set_askpass wrong &&
git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
@@ -86,14 +86,14 @@ test_expect_success 'http auth respects credential helper config' '
test_expect_success 'http auth can get username from config' '
test_config_global "credential.$HTTPD_URL.username" user@host &&
- set_askpass user@host &&
+ set_askpass wrong pass@host &&
git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
expect_askpass pass user@host
'
test_expect_success 'configured username does not override URL' '
test_config_global "credential.$HTTPD_URL.username" wrong &&
- set_askpass user@host &&
+ set_askpass wrong pass@host &&
git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
expect_askpass pass user@host
'
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
index afb439e09c..a124efe114 100755
--- a/t/t5551-http-fetch.sh
+++ b/t/t5551-http-fetch.sh
@@ -119,7 +119,7 @@ test_expect_success 'redirects re-root further requests' '
test_expect_success 'clone from password-protected repository' '
echo two >expect &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
expect_askpass both user@host &&
git --git-dir=smart-auth log -1 --format=%s >actual &&
@@ -137,7 +137,7 @@ test_expect_success 'clone from auth-only-for-push repository' '
test_expect_success 'clone from auth-only-for-objects repository' '
echo two >expect &&
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
expect_askpass both user@host &&
git --git-dir=half-auth log -1 --format=%s >actual &&
@@ -151,7 +151,7 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
'
test_expect_success 'redirects send auth to new location' '
- set_askpass user@host &&
+ set_askpass user@host pass@host &&
git -c credential.useHttpPath=true \
clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
expect_askpass both user@host auth/smart/repo.git
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 30a68335b3..39b3238da2 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -258,4 +258,43 @@ test_expect_success 'using reflog to find the fork point' '
test_cmp expect3 actual
'
+test_expect_success 'merge-base --octopus --all for complex tree' '
+ # Best common ancestor for JE, JAA and JDD is JC
+ # JE
+ # / |
+ # / |
+ # / |
+ # JAA / |
+ # |\ / |
+ # | \ | JDD |
+ # | \ |/ | |
+ # | JC JD |
+ # | | /| |
+ # | |/ | |
+ # JA | | |
+ # |\ /| | |
+ # X JB | X X
+ # \ \ | / /
+ # \__\|/___/
+ # J
+ test_commit J &&
+ test_commit JB &&
+ git reset --hard J &&
+ test_commit JC &&
+ git reset --hard J &&
+ test_commit JTEMP1 &&
+ test_merge JA JB &&
+ test_merge JAA JC &&
+ git reset --hard J &&
+ test_commit JTEMP2 &&
+ test_merge JD JB &&
+ test_merge JDD JC &&
+ git reset --hard J &&
+ test_commit JTEMP3 &&
+ test_merge JE JC &&
+ git rev-parse JC >expected &&
+ git merge-base --all --octopus JAA JDD JE >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 7d47984352..719a11673b 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/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 7ea14ced31..10b1452766 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -20,7 +20,17 @@ test_expect_success 'setup' '
test_commit start2 &&
git checkout master &&
git merge -m next start2 &&
- test_commit final
+ test_commit final &&
+
+ test_seq 40 |
+ while read i
+ do
+ git checkout --orphan "b$i" &&
+ test_tick &&
+ git commit --allow-empty -m "$i" &&
+ commit=$(git rev-parse --verify HEAD) &&
+ printf "$commit " >>.git/info/grafts
+ done
'
test_expect_success 'start is valid' '
@@ -79,6 +89,10 @@ test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
test_cmp expect actual
'
+test_expect_success 'large graft octopus' '
+ test_cmp_rev_output b31 "git rev-parse --verify b1^30"
+'
+
test_expect_success 'repack for next test' '
git repack -a -d
'
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
new file mode 100755
index 0000000000..62049be0c7
--- /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/t/t7006-pager.sh b/t/t7006-pager.sh
index ff2590849d..7fe3367b6b 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -37,6 +37,18 @@ test_expect_failure TTY 'pager runs from subdir' '
test_cmp expected actual
'
+test_expect_success TTY 'LESS and LV envvars are set for pagination' '
+ (
+ sane_unset LESS LV &&
+ PAGER="env >pager-env.out" &&
+ export PAGER &&
+
+ test_terminal git log
+ ) &&
+ grep ^LESS= pager-env.out &&
+ grep ^LV= pager-env.out
+'
+
test_expect_success TTY 'some commands do not use a pager' '
rm -f paginated.out &&
test_terminal git rev-list HEAD &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1cf78d5b11..1531c241c0 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -481,8 +481,6 @@ test_at_end_hook_ () {
test_done () {
GIT_EXIT_OK=t
- # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
- # output file
if test -z "$HARNESS_ACTIVE"
then
test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
diff --git a/tree-walk.c b/tree-walk.c
index 5ece8c3477..680afda060 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 ad3e9a04fe..164354dad7 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);
}