summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2012-05-22 15:34:46 -0700
committerJunio C Hamano <gitster@pobox.com>2012-05-22 15:34:46 -0700
commit423b5a104422d56645baf8229bfb496fa49c2a56 (patch)
treea7e020cab3c1a4d7c5b4eb04da55788e25bd6b81
parent963c0407f47f5c2c73deb67613f0ac5a0615889c (diff)
parent6b179adfe93d13c2f993668cd12eec52860486ac (diff)
downloadgit-423b5a104422d56645baf8229bfb496fa49c2a56.tar.gz
Merge branch 'fc/git-complete-helper' into fc/git-prompt-script
By Michael Haggerty (17) and others via Junio C Hamano (36) and Jeff King (1) * fc/git-complete-helper: (54 commits) completion: add new __git_complete helper Update draft release notes to 1.7.11 (11th batch) Git 1.7.10.2 document submdule.$name.update=none option for gitmodules The tenth batch of topics Update draft release notes to 1.7.10.2 checkout: do not corrupt HEAD on empty repo apply: remove lego in i18n string in gitdiff_verify_name dir: convert to strbuf status: refactor colopts handling status: respect "-b" for porcelain format status: fix null termination with "-b" status: refactor null_termination option commit: refactor option parsing Documentation/git-config: describe and clarify "--local <file>" option reflog-walk: tell explicit --date=default from not having --date at all clone: fix progress-regression grep.c: remove redundant line of code checkout (detached): truncate list of orphaned commits at the new HEAD t2020-checkout-detach: check for the number of orphaned commits ...
-rw-r--r--Documentation/RelNotes/1.7.10.2.txt39
-rw-r--r--Documentation/RelNotes/1.7.11.txt59
-rw-r--r--Documentation/git-config.txt14
-rw-r--r--Documentation/git-status.txt2
-rw-r--r--Documentation/git-submodule.txt7
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/gitmodules.txt5
-rw-r--r--Makefile12
-rw-r--r--archive-tar.c207
-rw-r--r--archive-zip.c200
-rw-r--r--archive.c28
-rw-r--r--archive.h10
-rw-r--r--bisect.c8
-rw-r--r--builtin/apply.c15
-rw-r--r--builtin/branch.c32
-rw-r--r--builtin/checkout.c15
-rw-r--r--builtin/clone.c2
-rw-r--r--builtin/commit.c144
-rw-r--r--builtin/rev-list.c1
-rw-r--r--commit.h1
-rwxr-xr-xcontrib/completion/git-completion.bash70
-rw-r--r--dir.c76
-rw-r--r--grep.c2
-rw-r--r--log-tree.c8
-rw-r--r--pretty.c1
-rw-r--r--reflog-walk.c22
-rw-r--r--reflog-walk.h4
-rw-r--r--refs.c376
-rw-r--r--streaming.c2
-rw-r--r--streaming.h2
-rwxr-xr-xt/t1050-large.sh12
-rwxr-xr-xt/t1411-reflog-show.sh61
-rwxr-xr-xt/t2015-checkout-unborn.sh11
-rwxr-xr-xt/t2020-checkout-detach.sh21
-rwxr-xr-xt/t5000-tar-tree.sh78
-rwxr-xr-xt/t7508-status.sh16
-rwxr-xr-xt/t9902-completion.sh2
-rw-r--r--wt-status.c35
-rw-r--r--wt-status.h8
39 files changed, 1084 insertions, 527 deletions
diff --git a/Documentation/RelNotes/1.7.10.2.txt b/Documentation/RelNotes/1.7.10.2.txt
index 04fe29404c..7a7e9d6fd1 100644
--- a/Documentation/RelNotes/1.7.10.2.txt
+++ b/Documentation/RelNotes/1.7.10.2.txt
@@ -14,6 +14,9 @@ Fixes since v1.7.10.1
* HTTP transport that requires authentication did not work correctly when
multiple connections are used simultaneously.
+ * Minor memory leak during unpack_trees (hence "merge" and "checkout"
+ to check out another branch) has been plugged.
+
* In the older days, the header "Conflicts:" in "cherry-pick" and "merge"
was separated by a blank line from the list of paths that follow for
readability, but when "merge" was rewritten in C, we lost it by
@@ -24,6 +27,9 @@ Fixes since v1.7.10.1
be both revision name and a pathname, even though $name can never be a
path in the context of the command.
+ * The "include.path" facility in the configuration mechanism added in
+ 1.7.10 forgot to interpret "~/path" and "~user/path" as it should.
+
* "git config --rename-section" to rename an existing section into a
bogus one did not check the new name.
@@ -33,11 +39,44 @@ Fixes since v1.7.10.1
* The report from "git fetch" said "new branch" even for a non branch
ref.
+ * The http-backend (the server side of the smart http transfer) used
+ to overwrite GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL with the
+ value obtained from REMOTE_USER unconditionally, making it
+ impossible for the server side site-specific customization to use
+ different identity sources to affect the names logged. It now uses
+ REMOTE_USER only as a fallback value.
+
+ * "log --graph" was not very friendly with "--stat" option and its
+ output had line breaks at wrong places.
+
* Octopus merge strategy did not reduce heads that are recorded in the
final commit correctly.
+ * "git push" over smart-http lost progress output a few releases ago;
+ this release resurrects it.
+
+ * The error and advice messages given by "git push" when it fails due
+ to non-ff were not very helpful to new users; it has been broken
+ into three cases, and each is given a separate advice message.
+
+ * The insn sheet given by "rebase -i" did not make it clear that the
+ insn lines can be re-ordered to affect the order of the commits in
+ the resulting history.
+
+ * "git repack" used to write out unreachable objects as loose objects
+ when repacking, even if such loose objects will immediately pruned
+ due to its age.
+
+ * A contrib script "rerere-train" did not work out of the box unless
+ user futzed with her $PATH.
+
+ * "git rev-parse --show-prefix" used to emit nothing when run at the
+ top-level of the working tree, but now it gives a blank line.
+
* The i18n of error message "git stash save" was not properly done.
+ * "git submodule" used a sed script that some platforms mishandled.
+
* When using a Perl script on a system where "perl" found on user's
$PATH could be ancient or otherwise broken, we allow builders to
specify the path to a good copy of Perl with $PERL_PATH. The
diff --git a/Documentation/RelNotes/1.7.11.txt b/Documentation/RelNotes/1.7.11.txt
index 8dfeddcd68..9ef7e18ca5 100644
--- a/Documentation/RelNotes/1.7.11.txt
+++ b/Documentation/RelNotes/1.7.11.txt
@@ -25,13 +25,15 @@ UI, Workflows & Features
tracking. Also "branch" learned the "-q"uiet option to squelch
informational message.
+ * Your build platform may support hardlinks but you may prefer not to
+ use them, e.g. when installing to DESTDIR to make a tarball and
+ untarring on a filesystem that has poor support for hardlinks.
+ There is a Makefile option NO_INSTALL_HARDLINKS for you.
+
* The smart-http backend used to always override GIT_COMMITTER_*
variables with REMOTE_USER and REMOTE_ADDR, but these variables are
now preserved when set.
- * "include.path" mechanism of the configuration files learned to
- understand "~/path" and "~user/path".
-
* "git am" learned the "--include" option, which is an opposite of
existing the "--exclude" option.
@@ -51,9 +53,6 @@ UI, Workflows & Features
* The "fmt-merge-msg" command learns to list the primary contributors
involved in the side topic you are merging.
- * The cases "git push" fails due to non-ff can be broken into three
- categories; each case is given a separate advise message.
-
* "git rebase" learned to optionally keep commits that do not
introduce any change in the original history.
@@ -83,27 +82,20 @@ Performance and Internal Implementation (please report possible regressions)
* An experimental "version 4" format of the index file has been
introduced to reduce on-disk footprint and I/O overhead.
+ * "git archive" learned to produce its output without reading the
+ blob object it writes out in memory in its entirety.
+
* The code to compute hash values for lines used by the internal diff
engine was optimized on little-endian machines, using the same
trick the kernel folks came up with.
* "git apply" had some memory leaks plugged.
- * "git repack" used to write out unreachable objects as loose objects
- when repacking, even if such loose objects will immediately pruned
- due to its age.
-
* Setting up a revision traversal with many starting points was
inefficient as these were placed in a date-order priority queue
one-by-one. Now they are collected in the queue unordered first,
and sorted immediately before getting used.
- * "git rev-parse --show-prefix" used to emit nothing when run at the
- top-level of the working tree, but now it gives a blank line.
-
- * Minor memory leak during unpack_trees (hence "merge" and "checkout"
- to check out another branch) has been plugged.
-
* More lower-level commands learned to use the streaming API to read
from the object store without keeping everything in core.
@@ -124,6 +116,30 @@ Unless otherwise noted, all the fixes since v1.7.10 in the maintenance
releases are contained in this release (see release notes to them for
details).
+ * The DWIM behaviour for "log --pretty=format:%gd -g" was somewhat
+ broken and gave undue precedence to configured log.date, causing
+ "git stash list" to show "stash@{time stamp string}".
+ (merge 55ccf85 jk/maint-reflog-walk-count-vs-time later to maint).
+
+ * Running "git checkout" on an unborn branch used to corrupt HEAD.
+ (merge 8338f77 ef/checkout-empty later to maint).
+
+ * When checking out another commit from an already detached state, we
+ used to report all commits that are not reachable from any of the
+ refs as lossage, but some of them might be reachable from the new
+ HEAD, and there is no need to warn about them.
+ (merge 5d88639 js/checkout-detach-count later to maint).
+
+ * Some time ago, "git clone" lost the progress output for its
+ "checkout" phase; when run without any "--quiet" option, it should
+ give progress to the lengthy operation.
+ (merge 8f63da1 ef/maint-clone-progress-fix later to maint).
+
+ * "git status --porcelain" ignored "--branch" option by mistake. The
+ output for "git status --branch -z" was also incorrect and did not
+ terminate the record for the current branch name with NUL as asked.
+ (merge d4a6bf1 jk/maint-status-porcelain-z-b later to maint).
+
* "git diff --stat" used to fully count a binary file with modified
execution bits whose contents is unmodified, which was not quite
right.
@@ -132,14 +148,3 @@ details).
NUL. The fix is not entirely correct when the output also asks for
--patch and/or --stat, though.
(merge fafd382 jk/maint-tformat-with-z later to maint).
-
- * "git push" over smart-http lost progress output a few releases ago.
- (merge e304aeb jk/maint-push-progress later to maint).
-
- * A contrib script "rerere-train" did not work out of the box unless
- user futzed with her $PATH.
- (merge 53876fc jc/rerere-train later to maint).
-
- * "log --graph" was not very friendly with "--stat" option and its
- output had line breaks at wrong places.
- (merge bafa16e lp/diffstat-with-graph later to maint).
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 81b03982e3..3f5d216a09 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -44,11 +44,15 @@ a "true" or "false" string for bool), or '--path', which does some
path expansion (see '--path' below). If no type specifier is passed, no
checks or transformations are performed on the value.
-The file-option can be one of '--system', '--global' or '--file'
-which specify where the values will be read from or written to.
-The default is to assume the config file of the current repository,
-.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
-(see <<FILES>>).
+When reading, the values are read from the system, global and
+repository local configuration files by default, and options
+'--system', '--global', '--local' and '--file <filename>' can be
+used to tell the command to read from only that location (see <<FILES>>).
+
+When writing, the new value is written to the repository local
+configuration file by default, and options '--system', '--global',
+'--file <filename>' can be used to tell the command to write to
+that location (you can say '--local' but that is the default).
This command will fail (with exit code ret) if:
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 2883a285ba..67e5f53a9e 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -184,7 +184,7 @@ order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL
and the terminating newline (but a space still separates the status
field from the first filename). Third, filenames containing special
characters are not specially formatted; no quoting or
-backslash-escaping is performed. Fourth, there is no branch line.
+backslash-escaping is performed.
CONFIGURATION
-------------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index c243ee552b..c83a856574 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -140,7 +140,8 @@ update::
checkout the commit specified in the index of the containing repository.
This will make the submodules HEAD be detached unless `--rebase` or
`--merge` is specified or the key `submodule.$name.update` is set to
- `rebase`, `merge` or `none`.
+ `rebase`, `merge` or `none`. `none` can be overriden by specifying
+ `--checkout`.
+
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
@@ -148,10 +149,6 @@ submodule with the `--init` option.
+
If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within.
-+
-If the configuration key `submodule.$name.update` is set to `none` the
-submodule with name `$name` will not be updated by default. This can be
-overriden by adding `--checkout` to the command.
summary::
Show commit summary between the given commit (defaults to HEAD) and
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 8527775988..c543213126 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.10.1/git.html[documentation for release 1.7.10.1]
+* link:v1.7.10.2/git.html[documentation for release 1.7.10.2]
* release notes for
+ link:RelNotes/1.7.10.2.txt[1.7.10.2],
link:RelNotes/1.7.10.1.txt[1.7.10.1],
link:RelNotes/1.7.10.txt[1.7.10].
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index 4e1fd52e7d..4effd78902 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -41,8 +41,11 @@ submodule.<name>.update::
the commit specified in the superproject. If 'merge', the commit
specified in the superproject will be merged into the current branch
in the submodule.
+ If 'none', the submodule with name `$name` will not be updated
+ by default.
+
This config option is overridden if 'git submodule update' is given
- the '--merge' or '--rebase' options.
+ the '--merge', '--rebase' or '--checkout' options.
submodule.<name>.fetchRecurseSubmodules::
This option can be used to control recursive fetching of this
diff --git a/Makefile b/Makefile
index 2fa7211055..3eda1e8c68 100644
--- a/Makefile
+++ b/Makefile
@@ -247,6 +247,9 @@ all::
# Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
# programs as a tar, where bin/ and libexec/ might be on different file systems.
#
+# Define NO_INSTALL_HARDLINKS if you prefer to use either symbolic links or
+# copies to install built-in git commands e.g. git-cat-file.
+#
# Define USE_NED_ALLOCATOR if you want to replace the platforms default
# memory allocators with the nedmalloc allocator written by Niall Douglas.
#
@@ -1835,6 +1838,10 @@ ifdef ASCIIDOC7
export ASCIIDOC7
endif
+ifdef NO_INSTALL_HARDLINKS
+ export NO_INSTALL_HARDLINKS
+endif
+
### profile feedback build
#
@@ -2574,19 +2581,21 @@ endif
{ test "$$bindir/" = "$$execdir/" || \
for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
$(RM) "$$execdir/$$p" && \
- test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
+ test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
done; \
} && \
for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
$(RM) "$$bindir/$$p" && \
+ test -z "$(NO_INSTALL_HARDLINKS)" && \
ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
done && \
for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
+ test -z "$(NO_INSTALL_HARDLINKS)" && \
ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
@@ -2594,6 +2603,7 @@ endif
remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
for p in $$remote_curl_aliases; do \
$(RM) "$$execdir/$$p" && \
+ test -z "$(NO_INSTALL_HARDLINKS)" && \
ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
diff --git a/archive-tar.c b/archive-tar.c
index 20af0051a3..93387ea336 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "tar.h"
#include "archive.h"
+#include "streaming.h"
#include "run-command.h"
#define RECORDSIZE (512)
@@ -30,10 +31,9 @@ static void write_if_needed(void)
* queues up writes, so that all our write(2) calls write exactly one
* full block; pads writes to RECORDSIZE
*/
-static void write_blocked(const void *data, unsigned long size)
+static void do_write_blocked(const void *data, unsigned long size)
{
const char *buf = data;
- unsigned long tail;
if (offset) {
unsigned long chunk = BLOCKSIZE - offset;
@@ -54,6 +54,11 @@ static void write_blocked(const void *data, unsigned long size)
memcpy(block + offset, buf, size);
offset += size;
}
+}
+
+static void finish_record(void)
+{
+ unsigned long tail;
tail = offset % RECORDSIZE;
if (tail) {
memset(block + offset, 0, RECORDSIZE - tail);
@@ -62,6 +67,12 @@ static void write_blocked(const void *data, unsigned long size)
write_if_needed();
}
+static void write_blocked(const void *data, unsigned long size)
+{
+ do_write_blocked(data, size);
+ finish_record();
+}
+
/*
* The end of tar archives is marked by 2*512 nul bytes and after that
* follows the rest of the block (if any).
@@ -78,6 +89,33 @@ static void write_trailer(void)
}
/*
+ * queues up writes, so that all our write(2) calls write exactly one
+ * full block; pads writes to RECORDSIZE
+ */
+static int stream_blocked(const unsigned char *sha1)
+{
+ struct git_istream *st;
+ enum object_type type;
+ unsigned long sz;
+ char buf[BLOCKSIZE];
+ ssize_t readlen;
+
+ st = open_istream(sha1, &type, &sz, NULL);
+ if (!st)
+ return error("cannot stream blob %s", sha1_to_hex(sha1));
+ for (;;) {
+ readlen = read_istream(st, buf, sizeof(buf));
+ if (readlen <= 0)
+ break;
+ do_write_blocked(buf, readlen);
+ }
+ close_istream(st);
+ if (!readlen)
+ finish_record();
+ return readlen;
+}
+
+/*
* pax extended header records have the format "%u %s=%s\n". %u contains
* the size of the whole string (including the %u), the first %s is the
* keyword, the second one is the value. This function constructs such a
@@ -123,56 +161,101 @@ static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen)
return i;
}
+static void prepare_header(struct archiver_args *args,
+ struct ustar_header *header,
+ unsigned int mode, unsigned long size)
+{
+ sprintf(header->mode, "%07o", mode & 07777);
+ sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0);
+ sprintf(header->mtime, "%011lo", (unsigned long) args->time);
+
+ sprintf(header->uid, "%07o", 0);
+ sprintf(header->gid, "%07o", 0);
+ strlcpy(header->uname, "root", sizeof(header->uname));
+ strlcpy(header->gname, "root", sizeof(header->gname));
+ sprintf(header->devmajor, "%07o", 0);
+ sprintf(header->devminor, "%07o", 0);
+
+ memcpy(header->magic, "ustar", 6);
+ memcpy(header->version, "00", 2);
+
+ sprintf(header->chksum, "%07o", ustar_header_chksum(header));
+}
+
+static int write_extended_header(struct archiver_args *args,
+ const unsigned char *sha1,
+ const void *buffer, unsigned long size)
+{
+ struct ustar_header header;
+ unsigned int mode;
+ memset(&header, 0, sizeof(header));
+ *header.typeflag = TYPEFLAG_EXT_HEADER;
+ mode = 0100666;
+ sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+ prepare_header(args, &header, mode, size);
+ write_blocked(&header, sizeof(header));
+ write_blocked(buffer, size);
+ return 0;
+}
+
static int write_tar_entry(struct archiver_args *args,
- const unsigned char *sha1, const char *path, size_t pathlen,
- unsigned int mode, void *buffer, unsigned long size)
+ const unsigned char *sha1,
+ const char *path, size_t pathlen,
+ unsigned int mode)
{
struct ustar_header header;
struct strbuf ext_header = STRBUF_INIT;
+ unsigned int old_mode = mode;
+ unsigned long size;
+ void *buffer;
int err = 0;
memset(&header, 0, sizeof(header));
- if (!sha1) {
- *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
- mode = 0100666;
- strcpy(header.name, "pax_global_header");
- } else if (!path) {
- *header.typeflag = TYPEFLAG_EXT_HEADER;
- mode = 0100666;
- sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+ if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
+ *header.typeflag = TYPEFLAG_DIR;
+ mode = (mode | 0777) & ~tar_umask;
+ } else if (S_ISLNK(mode)) {
+ *header.typeflag = TYPEFLAG_LNK;
+ mode |= 0777;
+ } else if (S_ISREG(mode)) {
+ *header.typeflag = TYPEFLAG_REG;
+ mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
} else {
- if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
- *header.typeflag = TYPEFLAG_DIR;
- mode = (mode | 0777) & ~tar_umask;
- } else if (S_ISLNK(mode)) {
- *header.typeflag = TYPEFLAG_LNK;
- mode |= 0777;
- } else if (S_ISREG(mode)) {
- *header.typeflag = TYPEFLAG_REG;
- mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
+ return error("unsupported file mode: 0%o (SHA1: %s)",
+ mode, sha1_to_hex(sha1));
+ }
+ if (pathlen > sizeof(header.name)) {
+ size_t plen = get_path_prefix(path, pathlen,
+ sizeof(header.prefix));
+ size_t rest = pathlen - plen - 1;
+ if (plen > 0 && rest <= sizeof(header.name)) {
+ memcpy(header.prefix, path, plen);
+ memcpy(header.name, path + plen + 1, rest);
} else {
- return error("unsupported file mode: 0%o (SHA1: %s)",
- mode, sha1_to_hex(sha1));
+ sprintf(header.name, "%s.data",
+ sha1_to_hex(sha1));
+ strbuf_append_ext_header(&ext_header, "path",
+ path, pathlen);
}
- if (pathlen > sizeof(header.name)) {
- size_t plen = get_path_prefix(path, pathlen,
- sizeof(header.prefix));
- size_t rest = pathlen - plen - 1;
- if (plen > 0 && rest <= sizeof(header.name)) {
- memcpy(header.prefix, path, plen);
- memcpy(header.name, path + plen + 1, rest);
- } else {
- sprintf(header.name, "%s.data",
- sha1_to_hex(sha1));
- strbuf_append_ext_header(&ext_header, "path",
- path, pathlen);
- }
- } else
- memcpy(header.name, path, pathlen);
+ } else
+ memcpy(header.name, path, pathlen);
+
+ if (S_ISREG(mode) && !args->convert &&
+ sha1_object_info(sha1, &size) == OBJ_BLOB &&
+ size > big_file_threshold)
+ buffer = NULL;
+ else if (S_ISLNK(mode) || S_ISREG(mode)) {
+ enum object_type type;
+ buffer = sha1_file_to_archive(args, path, sha1, old_mode, &type, &size);
+ if (!buffer)
+ return error("cannot read %s", sha1_to_hex(sha1));
+ } else {
+ buffer = NULL;
+ size = 0;
}
- if (S_ISLNK(mode) && buffer) {
+ if (S_ISLNK(mode)) {
if (size > sizeof(header.linkname)) {
sprintf(header.linkname, "see %s.paxheader",
sha1_to_hex(sha1));
@@ -182,32 +265,25 @@ static int write_tar_entry(struct archiver_args *args,
memcpy(header.linkname, buffer, size);
}
- sprintf(header.mode, "%07o", mode & 07777);
- sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
- sprintf(header.mtime, "%011lo", (unsigned long) args->time);
-
- sprintf(header.uid, "%07o", 0);
- sprintf(header.gid, "%07o", 0);
- strlcpy(header.uname, "root", sizeof(header.uname));
- strlcpy(header.gname, "root", sizeof(header.gname));
- sprintf(header.devmajor, "%07o", 0);
- sprintf(header.devminor, "%07o", 0);
-
- memcpy(header.magic, "ustar", 6);
- memcpy(header.version, "00", 2);
-
- sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
+ prepare_header(args, &header, mode, size);
if (ext_header.len > 0) {
- err = write_tar_entry(args, sha1, NULL, 0, 0, ext_header.buf,
- ext_header.len);
- if (err)
+ err = write_extended_header(args, sha1, ext_header.buf,
+ ext_header.len);
+ if (err) {
+ free(buffer);
return err;
+ }
}
strbuf_release(&ext_header);
write_blocked(&header, sizeof(header));
- if (S_ISREG(mode) && buffer && size > 0)
- write_blocked(buffer, size);
+ if (S_ISREG(mode) && size > 0) {
+ if (buffer)
+ write_blocked(buffer, size);
+ else
+ err = stream_blocked(sha1);
+ }
+ free(buffer);
return err;
}
@@ -215,11 +291,18 @@ static int write_global_extended_header(struct archiver_args *args)
{
const unsigned char *sha1 = args->commit_sha1;
struct strbuf ext_header = STRBUF_INIT;
- int err;
+ struct ustar_header header;
+ unsigned int mode;
+ int err = 0;
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
- err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf,
- ext_header.len);
+ memset(&header, 0, sizeof(header));
+ *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
+ mode = 0100666;
+ strcpy(header.name, "pax_global_header");
+ prepare_header(args, &header, mode, ext_header.len);
+ write_blocked(&header, sizeof(header));
+ write_blocked(ext_header.buf, ext_header.len);
strbuf_release(&ext_header);
return err;
}
diff --git a/archive-zip.c b/archive-zip.c
index 02d1f3787a..f5af81f904 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -3,6 +3,7 @@
*/
#include "cache.h"
#include "archive.h"
+#include "streaming.h"
static int zip_date;
static int zip_time;
@@ -15,6 +16,7 @@ static unsigned int zip_dir_offset;
static unsigned int zip_dir_entries;
#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+#define ZIP_STREAM (8)
struct zip_local_header {
unsigned char magic[4];
@@ -31,6 +33,14 @@ struct zip_local_header {
unsigned char _end[1];
};
+struct zip_data_desc {
+ unsigned char magic[4];
+ unsigned char crc32[4];
+ unsigned char compressed_size[4];
+ unsigned char size[4];
+ unsigned char _end[1];
+};
+
struct zip_dir_header {
unsigned char magic[4];
unsigned char creator_version[2];
@@ -70,6 +80,7 @@ struct zip_dir_trailer {
* we're interested in.
*/
#define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end)
+#define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end)
#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end)
#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end)
@@ -120,20 +131,59 @@ static void *zlib_deflate(void *data, unsigned long size,
return buffer;
}
+static void write_zip_data_desc(unsigned long size,
+ unsigned long compressed_size,
+ unsigned long crc)
+{
+ struct zip_data_desc trailer;
+
+ copy_le32(trailer.magic, 0x08074b50);
+ copy_le32(trailer.crc32, crc);
+ copy_le32(trailer.compressed_size, compressed_size);
+ copy_le32(trailer.size, size);
+ write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
+}
+
+static void set_zip_dir_data_desc(struct zip_dir_header *header,
+ unsigned long size,
+ unsigned long compressed_size,
+ unsigned long crc)
+{
+ copy_le32(header->crc32, crc);
+ copy_le32(header->compressed_size, compressed_size);
+ copy_le32(header->size, size);
+}
+
+static void set_zip_header_data_desc(struct zip_local_header *header,
+ unsigned long size,
+ unsigned long compressed_size,
+ unsigned long crc)
+{
+ copy_le32(header->crc32, crc);
+ copy_le32(header->compressed_size, compressed_size);
+ copy_le32(header->size, size);
+}
+
+#define STREAM_BUFFER_SIZE (1024 * 16)
+
static int write_zip_entry(struct archiver_args *args,
- const unsigned char *sha1, const char *path, size_t pathlen,
- unsigned int mode, void *buffer, unsigned long size)
+ const unsigned char *sha1,
+ const char *path, size_t pathlen,
+ unsigned int mode)
{
struct zip_local_header header;
struct zip_dir_header dirent;
unsigned long attr2;
unsigned long compressed_size;
- unsigned long uncompressed_size;
unsigned long crc;
unsigned long direntsize;
int method;
unsigned char *out;
void *deflated = NULL;
+ void *buffer;
+ struct git_istream *stream = NULL;
+ unsigned long flags = 0;
+ unsigned long size;
crc = crc32(0, NULL, 0);
@@ -146,24 +196,43 @@ static int write_zip_entry(struct archiver_args *args,
method = 0;
attr2 = 16;
out = NULL;
- uncompressed_size = 0;
+ size = 0;
compressed_size = 0;
+ buffer = NULL;
+ size = 0;
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
+ enum object_type type = sha1_object_info(sha1, &size);
+
method = 0;
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
(mode & 0111) ? ((mode) << 16) : 0;
- if (S_ISREG(mode) && args->compression_level != 0)
+ if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
method = 8;
- crc = crc32(crc, buffer, size);
- out = buffer;
- uncompressed_size = size;
compressed_size = size;
+
+ if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
+ size > big_file_threshold) {
+ stream = open_istream(sha1, &type, &size, NULL);
+ if (!stream)
+ return error("cannot stream blob %s",
+ sha1_to_hex(sha1));
+ flags |= ZIP_STREAM;
+ out = buffer = NULL;
+ } else {
+ buffer = sha1_file_to_archive(args, path, sha1, mode,
+ &type, &size);
+ if (!buffer)
+ return error("cannot read %s",
+ sha1_to_hex(sha1));
+ crc = crc32(crc, buffer, size);
+ out = buffer;
+ }
} else {
return error("unsupported file mode: 0%o (SHA1: %s)", mode,
sha1_to_hex(sha1));
}
- if (method == 8) {
+ if (buffer && method == 8) {
deflated = zlib_deflate(buffer, size, args->compression_level,
&compressed_size);
if (deflated && compressed_size - 6 < size) {
@@ -188,13 +257,11 @@ static int write_zip_entry(struct archiver_args *args,
copy_le16(dirent.creator_version,
S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
copy_le16(dirent.version, 10);
- copy_le16(dirent.flags, 0);
+ copy_le16(dirent.flags, flags);
copy_le16(dirent.compression_method, method);
copy_le16(dirent.mtime, zip_time);
copy_le16(dirent.mdate, zip_date);
- copy_le32(dirent.crc32, crc);
- copy_le32(dirent.compressed_size, compressed_size);
- copy_le32(dirent.size, uncompressed_size);
+ set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
copy_le16(dirent.filename_length, pathlen);
copy_le16(dirent.extra_length, 0);
copy_le16(dirent.comment_length, 0);
@@ -202,33 +269,120 @@ static int write_zip_entry(struct archiver_args *args,
copy_le16(dirent.attr1, 0);
copy_le32(dirent.attr2, attr2);
copy_le32(dirent.offset, zip_offset);
- memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
- zip_dir_offset += ZIP_DIR_HEADER_SIZE;
- memcpy(zip_dir + zip_dir_offset, path, pathlen);
- zip_dir_offset += pathlen;
- zip_dir_entries++;
copy_le32(header.magic, 0x04034b50);
copy_le16(header.version, 10);
- copy_le16(header.flags, 0);
+ copy_le16(header.flags, flags);
copy_le16(header.compression_method, method);
copy_le16(header.mtime, zip_time);
copy_le16(header.mdate, zip_date);
- copy_le32(header.crc32, crc);
- copy_le32(header.compressed_size, compressed_size);
- copy_le32(header.size, uncompressed_size);
+ if (flags & ZIP_STREAM)
+ set_zip_header_data_desc(&header, 0, 0, 0);
+ else
+ set_zip_header_data_desc(&header, size, compressed_size, crc);
copy_le16(header.filename_length, pathlen);
copy_le16(header.extra_length, 0);
write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
zip_offset += ZIP_LOCAL_HEADER_SIZE;
write_or_die(1, path, pathlen);
zip_offset += pathlen;
- if (compressed_size > 0) {
+ if (stream && method == 0) {
+ unsigned char buf[STREAM_BUFFER_SIZE];
+ ssize_t readlen;
+
+ for (;;) {
+ readlen = read_istream(stream, buf, sizeof(buf));
+ if (readlen <= 0)
+ break;
+ crc = crc32(crc, buf, readlen);
+ write_or_die(1, buf, readlen);
+ }
+ close_istream(stream);
+ if (readlen)
+ return readlen;
+
+ compressed_size = size;
+ zip_offset += compressed_size;
+
+ write_zip_data_desc(size, compressed_size, crc);
+ zip_offset += ZIP_DATA_DESC_SIZE;
+
+ set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+ } else if (stream && method == 8) {
+ unsigned char buf[STREAM_BUFFER_SIZE];
+ ssize_t readlen;
+ git_zstream zstream;
+ int result;
+ size_t out_len;
+ unsigned char compressed[STREAM_BUFFER_SIZE * 2];
+
+ memset(&zstream, 0, sizeof(zstream));
+ git_deflate_init(&zstream, args->compression_level);
+
+ compressed_size = 0;
+ zstream.next_out = compressed;
+ zstream.avail_out = sizeof(compressed);
+
+ for (;;) {
+ readlen = read_istream(stream, buf, sizeof(buf));
+ if (readlen <= 0)
+ break;
+ crc = crc32(crc, buf, readlen);
+
+ zstream.next_in = buf;
+ zstream.avail_in = readlen;
+ result = git_deflate(&zstream, 0);
+ if (result != Z_OK)
+ die("deflate error (%d)", result);
+ out = compressed;
+ if (!compressed_size)
+ out += 2;
+ out_len = zstream.next_out - out;
+
+ if (out_len > 0) {
+ write_or_die(1, out, out_len);
+ compressed_size += out_len;
+ zstream.next_out = compressed;
+ zstream.avail_out = sizeof(compressed);
+ }
+
+ }
+ close_istream(stream);
+ if (readlen)
+ return readlen;
+
+ zstream.next_in = buf;
+ zstream.avail_in = 0;
+ result = git_deflate(&zstream, Z_FINISH);
+ if (result != Z_STREAM_END)
+ die("deflate error (%d)", result);
+
+ git_deflate_end(&zstream);
+ out = compressed;
+ if (!compressed_size)
+ out += 2;
+ out_len = zstream.next_out - out - 4;
+ write_or_die(1, out, out_len);
+ compressed_size += out_len;
+ zip_offset += compressed_size;
+
+ write_zip_data_desc(size, compressed_size, crc);
+ zip_offset += ZIP_DATA_DESC_SIZE;
+
+ set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+ } else if (compressed_size > 0) {
write_or_die(1, out, compressed_size);
zip_offset += compressed_size;
}
free(deflated);
+ free(buffer);
+
+ memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
+ zip_dir_offset += ZIP_DIR_HEADER_SIZE;
+ memcpy(zip_dir + zip_dir_offset, path, pathlen);
+ zip_dir_offset += pathlen;
+ zip_dir_entries++;
return 0;
}
diff --git a/archive.c b/archive.c
index 1ee837d717..cd083eaf9a 100644
--- a/archive.c
+++ b/archive.c
@@ -59,12 +59,15 @@ static void format_subst(const struct commit *commit,
free(to_free);
}
-static void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
- unsigned int mode, enum object_type *type,
- unsigned long *sizep, const struct commit *commit)
+void *sha1_file_to_archive(const struct archiver_args *args,
+ const char *path, const unsigned char *sha1,
+ unsigned int mode, enum object_type *type,
+ unsigned long *sizep)
{
void *buffer;
+ const struct commit *commit = args->convert ? args->commit : NULL;
+ path += args->baselen;
buffer = read_sha1_file(sha1, type, sizep);
if (buffer && S_ISREG(mode)) {
struct strbuf buf = STRBUF_INIT;
@@ -109,12 +112,9 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
write_archive_entry_fn_t write_entry = c->write_entry;
struct git_attr_check check[2];
const char *path_without_prefix;
- int convert = 0;
int err;
- enum object_type type;
- unsigned long size;
- void *buffer;
+ args->convert = 0;
strbuf_reset(&path);
strbuf_grow(&path, PATH_MAX);
strbuf_add(&path, args->base, args->baselen);
@@ -126,28 +126,22 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {
if (ATTR_TRUE(check[0].value))
return 0;
- convert = ATTR_TRUE(check[1].value);
+ args->convert = ATTR_TRUE(check[1].value);
}
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
strbuf_addch(&path, '/');
if (args->verbose)
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
- err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
+ err = write_entry(args, sha1, path.buf, path.len, mode);
if (err)
return err;
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
}
- buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
- &type, &size, convert ? args->commit : NULL);
- if (!buffer)
- return error("cannot read %s", sha1_to_hex(sha1));
if (args->verbose)
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
- err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);
- free(buffer);
- return err;
+ return write_entry(args, sha1, path.buf, path.len, mode);
}
int write_archive_entries(struct archiver_args *args,
@@ -167,7 +161,7 @@ int write_archive_entries(struct archiver_args *args,
if (args->verbose)
fprintf(stderr, "%.*s\n", (int)len, args->base);
err = write_entry(args, args->tree->object.sha1, args->base,
- len, 040777, NULL, 0);
+ len, 040777);
if (err)
return err;
}
diff --git a/archive.h b/archive.h
index 2b0884f1ef..895afcdc7a 100644
--- a/archive.h
+++ b/archive.h
@@ -11,6 +11,7 @@ struct archiver_args {
const char **pathspec;
unsigned int verbose : 1;
unsigned int worktree_attributes : 1;
+ unsigned int convert : 1;
int compression_level;
};
@@ -27,11 +28,18 @@ extern void register_archiver(struct archiver *);
extern void init_tar_archiver(void);
extern void init_zip_archiver(void);
-typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size);
+typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
+ const unsigned char *sha1,
+ const char *path, size_t pathlen,
+ unsigned int mode);
extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix, const char *name_hint, int remote);
const char *archive_format_from_filename(const char *filename);
+extern void *sha1_file_to_archive(const struct archiver_args *args,
+ const char *path, const unsigned char *sha1,
+ unsigned int mode, enum object_type *type,
+ unsigned long *sizep);
#endif /* ARCHIVE_H */
diff --git a/bisect.c b/bisect.c
index 6e186e29cc..48acf73391 100644
--- a/bisect.c
+++ b/bisect.c
@@ -833,7 +833,7 @@ static int check_ancestors(const char *prefix)
*/
static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
{
- const char *filename = git_path("BISECT_ANCESTORS_OK");
+ char *filename = xstrdup(git_path("BISECT_ANCESTORS_OK"));
struct stat st;
int fd;
@@ -842,11 +842,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
/* Check if file BISECT_ANCESTORS_OK exists. */
if (!stat(filename, &st) && S_ISREG(st.st_mode))
- return;
+ goto done;
/* Bisecting with no good rev is ok. */
if (good_revs.nr == 0)
- return;
+ goto done;
/* Check if all good revs are ancestor of the bad rev. */
if (check_ancestors(prefix))
@@ -859,6 +859,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
filename, strerror(errno));
else
close(fd);
+ done:
+ free(filename);
}
/*
diff --git a/builtin/apply.c b/builtin/apply.c
index 725712d788..1793c33ee9 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -919,7 +919,10 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
* their names against any previous information, just
* to make sure..
*/
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
+#define DIFF_OLD_NAME 0
+#define DIFF_NEW_NAME 1
+
+static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
{
if (!orig_name && !isnull)
return find_name(line, NULL, p_value, TERM_TAB);
@@ -934,7 +937,9 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
another = find_name(line, NULL, p_value, TERM_TAB);
if (!another || memcmp(another, name, len + 1))
- die(_("git apply: bad git-diff - inconsistent %s filename on line %d"), oldnew, linenr);
+ die((side == DIFF_NEW_NAME) ?
+ _("git apply: bad git-diff - inconsistent new filename on line %d") :
+ _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
free(another);
return orig_name;
}
@@ -949,7 +954,8 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
static int gitdiff_oldname(const char *line, struct patch *patch)
{
char *orig = patch->old_name;
- patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
+ patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
+ DIFF_OLD_NAME);
if (orig != patch->old_name)
free(orig);
return 0;
@@ -958,7 +964,8 @@ static int gitdiff_oldname(const char *line, struct patch *patch)
static int gitdiff_newname(const char *line, struct patch *patch)
{
char *orig = patch->new_name;
- patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
+ patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
+ DIFF_NEW_NAME);
if (orig != patch->new_name)
free(orig);
return 0;
diff --git a/builtin/branch.c b/builtin/branch.c
index d51648fee4..0e060f2e4a 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -391,6 +391,7 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
int show_upstream_ref)
{
int ours, theirs;
+ char *ref = NULL;
struct branch *branch = branch_get(branch_name);
if (!stat_tracking_info(branch, &ours, &theirs)) {
@@ -401,16 +402,29 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
return;
}
- strbuf_addch(stat, '[');
if (show_upstream_ref)
- strbuf_addf(stat, "%s: ",
- shorten_unambiguous_ref(branch->merge[0]->dst, 0));
- if (!ours)
- strbuf_addf(stat, _("behind %d] "), theirs);
- else if (!theirs)
- strbuf_addf(stat, _("ahead %d] "), ours);
- else
- strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs);
+ ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ if (!ours) {
+ if (ref)
+ strbuf_addf(stat, _("[%s: behind %d]"), ref, theirs);
+ else
+ strbuf_addf(stat, _("[behind %d]"), theirs);
+
+ } else if (!theirs) {
+ if (ref)
+ strbuf_addf(stat, _("[%s: ahead %d]"), ref, ours);
+ else
+ strbuf_addf(stat, _("[ahead %d]"), ours);
+ } else {
+ if (ref)
+ strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
+ ref, ours, theirs);
+ else
+ strbuf_addf(stat, _("[ahead %d, behind %d]"),
+ ours, theirs);
+ }
+ strbuf_addch(stat, ' ');
+ free(ref);
}
static int matches_merge_filter(struct commit *commit)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 23fc56d88d..3ddda34f7a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -672,10 +672,10 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
* HEAD. If it is not reachable from any ref, this is the last chance
* for the user to do so without resorting to reflog.
*/
-static void orphaned_commit_warning(struct commit *commit)
+static void orphaned_commit_warning(struct commit *old, struct commit *new)
{
struct rev_info revs;
- struct object *object = &commit->object;
+ struct object *object = &old->object;
struct object_array refs;
init_revisions(&revs, NULL);
@@ -685,16 +685,17 @@ static void orphaned_commit_warning(struct commit *commit)
add_pending_object(&revs, object, sha1_to_hex(object->sha1));
for_each_ref(add_pending_uninteresting_ref, &revs);
+ add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
refs = revs.pending;
revs.leak_pending = 1;
if (prepare_revision_walk(&revs))
die(_("internal error in revision walk"));
- if (!(commit->object.flags & UNINTERESTING))
- suggest_reattach(commit, &revs);
+ if (!(old->object.flags & UNINTERESTING))
+ suggest_reattach(old, &revs);
else
- describe_detached_head(_("Previous HEAD position was"), commit);
+ describe_detached_head(_("Previous HEAD position was"), old);
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
free(refs.objects);
@@ -731,7 +732,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
}
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
- orphaned_commit_warning(old.commit);
+ orphaned_commit_warning(old.commit, new->commit);
update_refs_for_switch(opts, &old, new);
@@ -1091,7 +1092,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
if (opts.writeout_stage)
die(_("--ours/--theirs is incompatible with switching branches."));
- if (!new.commit) {
+ if (!new.commit && opts.new_branch) {
unsigned char rev[20];
int flag;
diff --git a/builtin/clone.c b/builtin/clone.c
index bbd5c96237..a4d8d25ee3 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -569,7 +569,7 @@ static int checkout(void)
opts.update = 1;
opts.merge = 1;
opts.fn = oneway_merge;
- opts.verbose_update = (option_verbosity > 0);
+ opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
opts.dst_index = &the_index;
diff --git a/builtin/commit.c b/builtin/commit.c
index a876a73e6b..a2ec73d738 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -89,7 +89,6 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit;
-static unsigned int colopts;
/*
* The default commit message cleanup mode will remove the lines
@@ -111,13 +110,11 @@ static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message = STRBUF_INIT;
-static int null_termination;
static enum {
STATUS_FORMAT_LONG,
STATUS_FORMAT_SHORT,
STATUS_FORMAT_PORCELAIN
} status_format = STATUS_FORMAT_LONG;
-static int status_show_branch;
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -131,59 +128,6 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
return 0;
}
-static struct option builtin_commit_options[] = {
- OPT__QUIET(&quiet, "suppress summary after successful commit"),
- OPT__VERBOSE(&verbose, "show diff in commit message template"),
-
- OPT_GROUP("Commit message options"),
- OPT_FILENAME('F', "file", &logfile, "read message from file"),
- OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
- OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
- OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
- OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
- OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
- OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
- OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
- OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
- OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
- OPT_FILENAME('t', "template", &template_file, "use specified template file"),
- OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
- OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
- OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
- "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- /* end commit message options */
-
- OPT_GROUP("Commit contents options"),
- OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
- OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
- OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
- OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
- OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
- OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
- OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
- OPT_SET_INT(0, "short", &status_format, "show status concisely",
- STATUS_FORMAT_SHORT),
- OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
- OPT_SET_INT(0, "porcelain", &status_format,
- "machine-readable output", STATUS_FORMAT_PORCELAIN),
- OPT_BOOLEAN('z', "null", &null_termination,
- "terminate entries with NUL"),
- OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
- OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
- { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- /* end commit contents options */
-
- { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
- "ok to record an empty change",
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
- { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
- "ok to record a change with an empty message",
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-
- OPT_END()
-};
-
static void determine_whence(struct wt_status *s)
{
if (file_exists(git_path("MERGE_HEAD")))
@@ -501,10 +445,10 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(s, null_termination, status_show_branch);
+ wt_shortstatus_print(s);
break;
case STATUS_FORMAT_PORCELAIN:
- wt_porcelain_print(s, null_termination);
+ wt_porcelain_print(s);
break;
case STATUS_FORMAT_LONG:
wt_status_print(s);
@@ -1037,6 +981,7 @@ static const char *read_commit_message(const char *name)
}
static int parse_and_validate_options(int argc, const char *argv[],
+ const struct option *options,
const char * const usage[],
const char *prefix,
struct commit *current_head,
@@ -1044,8 +989,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
{
int f = 0;
- argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
- 0);
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
if (force_author && !strchr(force_author, '>'))
force_author = find_author_by_nickname(force_author);
@@ -1130,7 +1074,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (all && argc > 0)
die(_("Paths with -a does not make sense."));
- if (null_termination && status_format == STATUS_FORMAT_LONG)
+ if (s->null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
if (status_format != STATUS_FORMAT_LONG)
dry_run = 1;
@@ -1176,7 +1120,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
struct wt_status *s = cb;
if (!prefixcmp(k, "column."))
- return git_column_config(k, v, "status", &colopts);
+ return git_column_config(k, v, "status", &s->colopts);
if (!strcmp(k, "status.submodulesummary")) {
int is_bool;
s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1219,19 +1163,19 @@ static int git_status_config(const char *k, const char *v, void *cb)
int cmd_status(int argc, const char **argv, const char *prefix)
{
- struct wt_status s;
+ static struct wt_status s;
int fd;
unsigned char sha1[20];
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose, "be verbose"),
OPT_SET_INT('s', "short", &status_format,
"show status concisely", STATUS_FORMAT_SHORT),
- OPT_BOOLEAN('b', "branch", &status_show_branch,
+ OPT_BOOLEAN('b', "branch", &s.show_branch,
"show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"machine-readable output",
STATUS_FORMAT_PORCELAIN),
- OPT_BOOLEAN('z', "null", &null_termination,
+ OPT_BOOLEAN('z', "null", &s.null_termination,
"terminate entries with NUL"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
"mode",
@@ -1242,7 +1186,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_COLUMN(0, "column", &colopts, "list untracked files in columns"),
+ OPT_COLUMN(0, "column", &s.colopts, "list untracked files in columns"),
OPT_END(),
};
@@ -1256,10 +1200,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
- finalize_colopts(&colopts, -1);
- s.colopts = colopts;
+ finalize_colopts(&s.colopts, -1);
- if (null_termination && status_format == STATUS_FORMAT_LONG)
+ if (s.null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
handle_untracked_files_arg(&s);
@@ -1284,10 +1227,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(&s, null_termination, status_show_branch);
+ wt_shortstatus_print(&s);
break;
case STATUS_FORMAT_PORCELAIN:
- wt_porcelain_print(&s, null_termination);
+ wt_porcelain_print(&s);
break;
case STATUS_FORMAT_LONG:
s.verbose = verbose;
@@ -1422,6 +1365,60 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
int cmd_commit(int argc, const char **argv, const char *prefix)
{
+ static struct wt_status s;
+ static struct option builtin_commit_options[] = {
+ OPT__QUIET(&quiet, "suppress summary after successful commit"),
+ OPT__VERBOSE(&verbose, "show diff in commit message template"),
+
+ OPT_GROUP("Commit message options"),
+ OPT_FILENAME('F', "file", &logfile, "read message from file"),
+ OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+ OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+ OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+ OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+ OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+ OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+ OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
+ OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
+ OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
+ OPT_FILENAME('t', "template", &template_file, "use specified template file"),
+ OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
+ OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+ OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+ "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ /* end commit message options */
+
+ OPT_GROUP("Commit contents options"),
+ OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+ OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+ OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+ OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
+ OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
+ OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+ OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
+ OPT_SET_INT(0, "short", &status_format, "show status concisely",
+ STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"),
+ OPT_SET_INT(0, "porcelain", &status_format,
+ "machine-readable output", STATUS_FORMAT_PORCELAIN),
+ OPT_BOOLEAN('z', "null", &s.null_termination,
+ "terminate entries with NUL"),
+ OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+ OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
+ { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ /* end commit contents options */
+
+ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+ "ok to record an empty change",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+ "ok to record a change with an empty message",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
+ OPT_END()
+ };
+
struct strbuf sb = STRBUF_INIT;
struct strbuf author_ident = STRBUF_INIT;
const char *index_file, *reflog_msg;
@@ -1431,7 +1428,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
int allow_fast_forward = 1;
- struct wt_status s;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
@@ -1441,6 +1437,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
git_config(git_commit_config, &s);
determine_whence(&s);
+ s.colopts = 0;
if (get_sha1("HEAD", sha1))
current_head = NULL;
@@ -1449,7 +1446,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!current_head || parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
- argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+ argc = parse_and_validate_options(argc, argv, builtin_commit_options,
+ builtin_commit_usage,
prefix, current_head, &s);
if (dry_run)
return dry_run_commit(argc, argv, prefix, current_head, &s);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 4c4d404afc..ff5a38372d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -109,6 +109,7 @@ static void show_commit(struct commit *commit, void *data)
struct pretty_print_context ctx = {0};
ctx.abbrev = revs->abbrev;
ctx.date_mode = revs->date_mode;
+ ctx.date_mode_explicit = revs->date_mode_explicit;
ctx.fmt = revs->commit_format;
pretty_print_commit(&ctx, commit, &buf);
if (revs->graph) {
diff --git a/commit.h b/commit.h
index ccaa20b262..d617fa3f28 100644
--- a/commit.h
+++ b/commit.h
@@ -84,6 +84,7 @@ struct pretty_print_context {
const char *after_subject;
int preserve_subject;
enum date_mode date_mode;
+ unsigned date_mode_explicit:1;
int need_8bit_cte;
int show_notes;
struct reflog_walk_info *reflog_info;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9f56ec7a6b..d60bb8ac8f 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2603,21 +2603,6 @@ _git ()
{
local i c=1 command __git_dir
- if [[ -n ${ZSH_VERSION-} ]]; then
- emulate -L bash
- setopt KSH_TYPESET
-
- # workaround zsh's bug that leaves 'words' as a special
- # variable in versions < 4.3.12
- typeset -h words
-
- # workaround zsh's bug that quotes spaces in the COMPREPLY
- # array if IFS doesn't contain spaces.
- typeset -h IFS
- fi
-
- local cur words cword prev
- _get_comp_words_by_ref -n =: cur words cword prev
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
@@ -2667,22 +2652,6 @@ _git ()
_gitk ()
{
- if [[ -n ${ZSH_VERSION-} ]]; then
- emulate -L bash
- setopt KSH_TYPESET
-
- # workaround zsh's bug that leaves 'words' as a special
- # variable in versions < 4.3.12
- typeset -h words
-
- # workaround zsh's bug that quotes spaces in the COMPREPLY
- # array if IFS doesn't contain spaces.
- typeset -h IFS
- fi
-
- local cur words cword prev
- _get_comp_words_by_ref -n =: cur words cword prev
-
__git_has_doubledash && return
local g="$(__gitdir)"
@@ -2703,16 +2672,43 @@ _gitk ()
__git_complete_revlist
}
-complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
- || complete -o default -o nospace -F _git git
-complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
- || complete -o default -o nospace -F _gitk gitk
+__git_func_wrap ()
+{
+ if [[ -n ${ZSH_VERSION-} ]]; then
+ emulate -L bash
+ setopt KSH_TYPESET
+
+ # workaround zsh's bug that leaves 'words' as a special
+ # variable in versions < 4.3.12
+ typeset -h words
+
+ # workaround zsh's bug that quotes spaces in the COMPREPLY
+ # array if IFS doesn't contain spaces.
+ typeset -h IFS
+ fi
+ local cur words cword prev
+ _get_comp_words_by_ref -n =: cur words cword prev
+ $1
+}
+
+# Setup completion for certain functions defined above by setting common
+# variables and workarounds.
+# This is NOT a public function; use at your own risk.
+__git_complete ()
+{
+ local wrapper="__git_wrap${2}"
+ eval "$wrapper () { __git_func_wrap $2 ; }"
+ complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
+ || complete -o default -o nospace -F $wrapper $1
+}
+
+__git_complete git _git
+__git_complete gitk _gitk
# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
- || complete -o default -o nospace -F _git git.exe
+__git_complete git.exe _git
fi
diff --git a/dir.c b/dir.c
index e98760c72d..c6a98cc8dd 100644
--- a/dir.c
+++ b/dir.c
@@ -873,14 +873,14 @@ enum path_treatment {
};
static enum path_treatment treat_one_path(struct dir_struct *dir,
- char *path, int *len,
+ struct strbuf *path,
const struct path_simplify *simplify,
int dtype, struct dirent *de)
{
- int exclude = excluded(dir, path, &dtype);
+ int exclude = excluded(dir, path->buf, &dtype);
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
- && exclude_matches_pathspec(path, *len, simplify))
- dir_add_ignored(dir, path, *len);
+ && exclude_matches_pathspec(path->buf, path->len, simplify))
+ dir_add_ignored(dir, path->buf, path->len);
/*
* Excluded? If we don't explicitly want to show
@@ -890,7 +890,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
return path_ignored;
if (dtype == DT_UNKNOWN)
- dtype = get_dtype(de, path, *len);
+ dtype = get_dtype(de, path->buf, path->len);
/*
* Do we want to see just the ignored files?
@@ -907,9 +907,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
default:
return path_ignored;
case DT_DIR:
- memcpy(path + *len, "/", 2);
- (*len)++;
- switch (treat_directory(dir, path, *len, simplify)) {
+ strbuf_addch(path, '/');
+ switch (treat_directory(dir, path->buf, path->len, simplify)) {
case show_directory:
if (exclude != !!(dir->flags
& DIR_SHOW_IGNORED))
@@ -930,26 +929,21 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
static enum path_treatment treat_path(struct dir_struct *dir,
struct dirent *de,
- char *path, int path_max,
+ struct strbuf *path,
int baselen,
- const struct path_simplify *simplify,
- int *len)
+ const struct path_simplify *simplify)
{
int dtype;
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
return path_ignored;
- *len = strlen(de->d_name);
- /* Ignore overly long pathnames! */
- if (*len + baselen + 8 > path_max)
- return path_ignored;
- memcpy(path + baselen, de->d_name, *len + 1);
- *len += baselen;
- if (simplify_away(path, *len, simplify))
+ strbuf_setlen(path, baselen);
+ strbuf_addstr(path, de->d_name);
+ if (simplify_away(path->buf, path->len, simplify))
return path_ignored;
dtype = DTYPE(de);
- return treat_one_path(dir, path, len, simplify, dtype, de);
+ return treat_one_path(dir, path, simplify, dtype, de);
}
/*
@@ -969,19 +963,19 @@ static int read_directory_recursive(struct dir_struct *dir,
DIR *fdir = opendir(*base ? base : ".");
int contents = 0;
struct dirent *de;
- char path[PATH_MAX + 1];
+ struct strbuf path = STRBUF_INIT;
if (!fdir)
return 0;
- memcpy(path, base, baselen);
+ strbuf_add(&path, base, baselen);
while ((de = readdir(fdir)) != NULL) {
- int len;
- switch (treat_path(dir, de, path, sizeof(path),
- baselen, simplify, &len)) {
+ switch (treat_path(dir, de, &path, baselen, simplify)) {
case path_recurse:
- contents += read_directory_recursive(dir, path, len, 0, simplify);
+ contents += read_directory_recursive(dir, path.buf,
+ path.len, 0,
+ simplify);
continue;
case path_ignored:
continue;
@@ -992,10 +986,11 @@ static int read_directory_recursive(struct dir_struct *dir,
if (check_only)
goto exit_early;
else
- dir_add_name(dir, path, len);
+ dir_add_name(dir, path.buf, path.len);
}
exit_early:
closedir(fdir);
+ strbuf_release(&path);
return contents;
}
@@ -1058,8 +1053,8 @@ static int treat_leading_path(struct dir_struct *dir,
const char *path, int len,
const struct path_simplify *simplify)
{
- char pathbuf[PATH_MAX];
- int baselen, blen;
+ struct strbuf sb = STRBUF_INIT;
+ int baselen, rc = 0;
const char *cp;
while (len && path[len - 1] == '/')
@@ -1074,19 +1069,22 @@ static int treat_leading_path(struct dir_struct *dir,
baselen = len;
else
baselen = cp - path;
- memcpy(pathbuf, path, baselen);
- pathbuf[baselen] = '\0';
- if (!is_directory(pathbuf))
- return 0;
- if (simplify_away(pathbuf, baselen, simplify))
- return 0;
- blen = baselen;
- if (treat_one_path(dir, pathbuf, &blen, simplify,
+ strbuf_setlen(&sb, 0);
+ strbuf_add(&sb, path, baselen);
+ if (!is_directory(sb.buf))
+ break;
+ if (simplify_away(sb.buf, sb.len, simplify))
+ break;
+ if (treat_one_path(dir, &sb, simplify,
DT_DIR, NULL) == path_ignored)
- return 0; /* do not recurse into it */
- if (len <= baselen)
- return 1; /* finished checking */
+ break; /* do not recurse into it */
+ if (len <= baselen) {
+ rc = 1;
+ break; /* finished checking */
+ }
}
+ strbuf_release(&sb);
+ return rc;
}
int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
diff --git a/grep.c b/grep.c
index 190139cfcd..f8ffa46209 100644
--- a/grep.c
+++ b/grep.c
@@ -318,7 +318,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
if (!opt->header_list)
return NULL;
- p = opt->header_list;
+
for (p = opt->header_list; p; p = p->next) {
if (p->token != GREP_PATTERN_HEAD)
die("bug: a non-header pattern in grep header list.");
diff --git a/log-tree.c b/log-tree.c
index 44f0268372..376d973176 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -629,10 +629,9 @@ void show_log(struct rev_info *opt)
* graph info here.
*/
show_reflog_message(opt->reflog_info,
- opt->commit_format == CMIT_FMT_ONELINE,
- opt->date_mode_explicit ?
- opt->date_mode :
- DATE_NORMAL);
+ opt->commit_format == CMIT_FMT_ONELINE,
+ opt->date_mode,
+ opt->date_mode_explicit);
if (opt->commit_format == CMIT_FMT_ONELINE)
return;
}
@@ -652,6 +651,7 @@ void show_log(struct rev_info *opt)
if (ctx.need_8bit_cte >= 0)
ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
ctx.date_mode = opt->date_mode;
+ ctx.date_mode_explicit = opt->date_mode_explicit;
ctx.abbrev = opt->diffopt.abbrev;
ctx.after_subject = extra_headers;
ctx.preserve_subject = opt->preserve_subject;
diff --git a/pretty.c b/pretty.c
index f2dee308b8..02a0a2bb43 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1010,6 +1010,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
get_reflog_selector(sb,
c->pretty_ctx->reflog_info,
c->pretty_ctx->date_mode,
+ c->pretty_ctx->date_mode_explicit,
(placeholder[1] == 'd'));
return 2;
case 's': /* reflog message */
diff --git a/reflog-walk.c b/reflog-walk.c
index 86d18843f5..b2fbdb2392 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -126,7 +126,12 @@ static void add_commit_info(struct commit *commit, void *util,
}
struct commit_reflog {
- int flag, recno;
+ int recno;
+ enum selector_type {
+ SELECTOR_NONE,
+ SELECTOR_INDEX,
+ SELECTOR_DATE
+ } selector;
struct complete_reflogs *reflogs;
};
@@ -150,6 +155,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
struct complete_reflogs *reflogs;
char *branch, *at = strchr(name, '@');
struct commit_reflog *commit_reflog;
+ enum selector_type selector = SELECTOR_NONE;
if (commit->object.flags & UNINTERESTING)
die ("Cannot walk reflogs for %s", name);
@@ -162,7 +168,10 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
if (*ep != '}') {
recno = -1;
timestamp = approxidate(at + 2);
+ selector = SELECTOR_DATE;
}
+ else
+ selector = SELECTOR_INDEX;
} else
recno = 0;
@@ -200,7 +209,6 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
if (recno < 0) {
- commit_reflog->flag = 1;
commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
if (commit_reflog->recno < 0) {
free(branch);
@@ -209,6 +217,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
}
} else
commit_reflog->recno = reflogs->nr - recno - 1;
+ commit_reflog->selector = selector;
commit_reflog->reflogs = reflogs;
add_commit_info(commit, commit_reflog, &info->reflogs);
@@ -247,7 +256,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- enum date_mode dmode,
+ enum date_mode dmode, int force_date,
int shorten)
{
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -267,7 +276,8 @@ void get_reflog_selector(struct strbuf *sb,
}
strbuf_addf(sb, "%s@{", printed_ref);
- if (commit_reflog->flag || dmode) {
+ if (commit_reflog->selector == SELECTOR_DATE ||
+ (commit_reflog->selector == SELECTOR_NONE && force_date)) {
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode));
} else {
@@ -308,7 +318,7 @@ const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
}
void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
- enum date_mode dmode)
+ enum date_mode dmode, int force_date)
{
if (reflog_info && reflog_info->last_commit_reflog) {
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -316,7 +326,7 @@ void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
struct strbuf selector = STRBUF_INIT;
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
- get_reflog_selector(&selector, reflog_info, dmode, 0);
+ get_reflog_selector(&selector, reflog_info, dmode, force_date, 0);
if (oneline) {
printf("%s: %s", selector.buf, info->message);
}
diff --git a/reflog-walk.h b/reflog-walk.h
index afb1ae3fde..50265f51c5 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -11,13 +11,13 @@ extern int add_reflog_for_walk(struct reflog_walk_info *info,
extern void fake_reflog_parent(struct reflog_walk_info *info,
struct commit *commit);
extern void show_reflog_message(struct reflog_walk_info *info, int,
- enum date_mode);
+ enum date_mode, int force_date);
extern void get_reflog_message(struct strbuf *sb,
struct reflog_walk_info *reflog_info);
extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
extern void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- enum date_mode dmode,
+ enum date_mode dmode, int force_date,
int shorten);
#endif
diff --git a/refs.c b/refs.c
index a5802e1902..d6bdb47ad6 100644
--- a/refs.c
+++ b/refs.c
@@ -101,11 +101,45 @@ int check_refname_format(const char *refname, int flags)
struct ref_entry;
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a single cached reference. This data structure only
+ * occurs embedded in a union in struct ref_entry, and only when
+ * (ref_entry->flag & REF_DIR) is zero.
+ */
struct ref_value {
unsigned char sha1[20];
unsigned char peeled[20];
};
+struct ref_cache;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a level in the hierarchy of references. This data
+ * structure only occurs embedded in a union in struct ref_entry, and
+ * only when (ref_entry.flag & REF_DIR) is set. In that case,
+ * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
+ * in the directory have already been read:
+ *
+ * (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
+ * or packed references, already read.
+ *
+ * (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
+ * references that hasn't been read yet (nor has any of its
+ * subdirectories).
+ *
+ * Entries within a directory are stored within a growable array of
+ * pointers to ref_entries (entries, nr, alloc). Entries 0 <= i <
+ * sorted are sorted by their component name in strcmp() order and the
+ * remaining entries are unsorted.
+ *
+ * Loose references are read lazily, one directory at a time. When a
+ * directory of loose references is read, then all of the references
+ * in that directory are stored, and REF_INCOMPLETE stubs are created
+ * for any subdirectories, but the subdirectories themselves are not
+ * read. The reading is triggered by get_ref_dir().
+ */
struct ref_dir {
int nr, alloc;
@@ -117,24 +151,41 @@ struct ref_dir {
*/
int sorted;
+ /* A pointer to the ref_cache that contains this ref_dir. */
+ struct ref_cache *ref_cache;
+
struct ref_entry **entries;
};
/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
#define REF_KNOWS_PEELED 0x08
+
+/* ref_entry represents a directory of references */
#define REF_DIR 0x10
/*
+ * Entry has not yet been read from disk (used only for REF_DIR
+ * entries representing loose references)
+ */
+#define REF_INCOMPLETE 0x20
+
+/*
* A ref_entry represents either a reference or a "subdirectory" of
- * references. Each directory in the reference namespace is
- * represented by a ref_entry with (flags & REF_DIR) set and
- * containing a subdir member that holds the entries in that
- * directory. References are represented by a ref_entry with (flags &
- * REF_DIR) unset and a value member that describes the reference's
- * value. The flag member is at the ref_entry level, but it is also
- * needed to interpret the contents of the value field (in other
- * words, a ref_value object is not very much use without the
- * enclosing ref_entry).
+ * references.
+ *
+ * Each directory in the reference namespace is represented by a
+ * ref_entry with (flags & REF_DIR) set and containing a subdir member
+ * that holds the entries in that directory that have been read so
+ * far. If (flags & REF_INCOMPLETE) is set, then the directory and
+ * its subdirectories haven't been read yet. REF_INCOMPLETE is only
+ * used for loose reference directories.
+ *
+ * References are represented by a ref_entry with (flags & REF_DIR)
+ * unset and a value member that describes the reference's value. The
+ * flag member is at the ref_entry level, but it is also needed to
+ * interpret the contents of the value field (in other words, a
+ * ref_value object is not very much use without the enclosing
+ * ref_entry).
*
* Reference names cannot end with slash and directories' names are
* always stored with a trailing slash (except for the top-level
@@ -171,6 +222,20 @@ struct ref_entry {
char name[FLEX_ARRAY];
};
+static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+
+static struct ref_dir *get_ref_dir(struct ref_entry *entry)
+{
+ struct ref_dir *dir;
+ assert(entry->flag & REF_DIR);
+ dir = &entry->u.subdir;
+ if (entry->flag & REF_INCOMPLETE) {
+ read_loose_refs(entry->name, dir);
+ entry->flag &= ~REF_INCOMPLETE;
+ }
+ return dir;
+}
+
static struct ref_entry *create_ref_entry(const char *refname,
const unsigned char *sha1, int flag,
int check_name)
@@ -195,7 +260,7 @@ static void clear_ref_dir(struct ref_dir *dir);
static void free_ref_entry(struct ref_entry *entry)
{
if (entry->flag & REF_DIR)
- clear_ref_dir(&entry->u.subdir);
+ clear_ref_dir(get_ref_dir(entry));
free(entry);
}
@@ -228,13 +293,15 @@ static void clear_ref_dir(struct ref_dir *dir)
* dirname is the name of the directory with a trailing slash (e.g.,
* "refs/heads/") or "" for the top-level directory.
*/
-static struct ref_entry *create_dir_entry(const char *dirname)
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+ const char *dirname, int incomplete)
{
struct ref_entry *direntry;
int len = strlen(dirname);
direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
memcpy(direntry->name, dirname, len + 1);
- direntry->flag = REF_DIR;
+ direntry->u.subdir.ref_cache = ref_cache;
+ direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
return direntry;
}
@@ -250,7 +317,7 @@ static void sort_ref_dir(struct ref_dir *dir);
/*
* Return the entry with the given refname from the ref_dir
* (non-recursively), sorting dir if necessary. Return NULL if no
- * such entry is found.
+ * such entry is found. dir must already be complete.
*/
static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)
{
@@ -277,38 +344,60 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname
}
/*
+ * Search for a directory entry directly within dir (without
+ * recursing). Sort dir if necessary. subdirname must be a directory
+ * name (i.e., end in '/'). If mkdir is set, then create the
+ * directory if it is missing; otherwise, return NULL if the desired
+ * directory cannot be found. dir must already be complete.
+ */
+static struct ref_dir *search_for_subdir(struct ref_dir *dir,
+ const char *subdirname, int mkdir)
+{
+ struct ref_entry *entry = search_ref_dir(dir, subdirname);
+ if (!entry) {
+ if (!mkdir)
+ return NULL;
+ /*
+ * Since dir is complete, the absence of a subdir
+ * means that the subdir really doesn't exist;
+ * therefore, create an empty record for it but mark
+ * the record complete.
+ */
+ entry = create_dir_entry(dir->ref_cache, subdirname, 0);
+ add_entry_to_dir(dir, entry);
+ }
+ return get_ref_dir(entry);
+}
+
+/*
* If refname is a reference name, find the ref_dir within the dir
* tree that should hold refname. If refname is a directory name
* (i.e., ends in '/'), then return that ref_dir itself. dir must
- * represent the top-level directory. Sort ref_dirs and recurse into
- * subdirectories as necessary. If mkdir is set, then create any
- * missing directories; otherwise, return NULL if the desired
- * directory cannot be found.
+ * represent the top-level directory and must already be complete.
+ * Sort ref_dirs and recurse into subdirectories as necessary. If
+ * mkdir is set, then create any missing directories; otherwise,
+ * return NULL if the desired directory cannot be found.
*/
static struct ref_dir *find_containing_dir(struct ref_dir *dir,
const char *refname, int mkdir)
{
- char *refname_copy = xstrdup(refname);
- char *slash;
- struct ref_entry *entry;
- for (slash = strchr(refname_copy, '/'); slash; slash = strchr(slash + 1, '/')) {
- char tmp = slash[1];
- slash[1] = '\0';
- entry = search_ref_dir(dir, refname_copy);
- if (!entry) {
- if (!mkdir) {
- dir = NULL;
- break;
- }
- entry = create_dir_entry(refname_copy);
- add_entry_to_dir(dir, entry);
+ struct strbuf dirname;
+ const char *slash;
+ strbuf_init(&dirname, PATH_MAX);
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ struct ref_dir *subdir;
+ strbuf_add(&dirname,
+ refname + dirname.len,
+ (slash + 1) - (refname + dirname.len));
+ subdir = search_for_subdir(dir, dirname.buf, mkdir);
+ if (!subdir) {
+ dir = NULL;
+ break;
}
- slash[1] = tmp;
- assert(entry->flag & REF_DIR);
- dir = &entry->u.subdir;
+ dir = subdir;
}
- free(refname_copy);
+ strbuf_release(&dirname);
return dir;
}
@@ -434,8 +523,9 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
struct ref_entry *entry = dir->entries[i];
int retval;
if (entry->flag & REF_DIR) {
- sort_ref_dir(&entry->u.subdir);
- retval = do_for_each_ref_in_dir(&entry->u.subdir, 0,
+ struct ref_dir *subdir = get_ref_dir(entry);
+ sort_ref_dir(subdir);
+ retval = do_for_each_ref_in_dir(subdir, 0,
base, fn, trim, flags, cb_data);
} else {
retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
@@ -480,10 +570,12 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
if (cmp == 0) {
if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
/* Both are directories; descend them in parallel. */
- sort_ref_dir(&e1->u.subdir);
- sort_ref_dir(&e2->u.subdir);
+ struct ref_dir *subdir1 = get_ref_dir(e1);
+ struct ref_dir *subdir2 = get_ref_dir(e2);
+ sort_ref_dir(subdir1);
+ sort_ref_dir(subdir2);
retval = do_for_each_ref_in_dirs(
- &e1->u.subdir, &e2->u.subdir,
+ subdir1, subdir2,
base, fn, trim, flags, cb_data);
i1++;
i2++;
@@ -506,9 +598,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
i2++;
}
if (e->flag & REF_DIR) {
- sort_ref_dir(&e->u.subdir);
+ struct ref_dir *subdir = get_ref_dir(e);
+ sort_ref_dir(subdir);
retval = do_for_each_ref_in_dir(
- &e->u.subdir, 0,
+ subdir, 0,
base, fn, trim, flags, cb_data);
} else {
retval = do_one_ref(base, fn, trim, flags, cb_data, e);
@@ -592,26 +685,26 @@ static int is_refname_available(const char *refname, const char *oldrefname,
*/
static struct ref_cache {
struct ref_cache *next;
- char did_loose;
- char did_packed;
- struct ref_dir loose;
- struct ref_dir packed;
+ struct ref_entry *loose;
+ struct ref_entry *packed;
/* The submodule name, or "" for the main repo. */
char name[FLEX_ARRAY];
} *ref_cache;
static void clear_packed_ref_cache(struct ref_cache *refs)
{
- if (refs->did_packed)
- clear_ref_dir(&refs->packed);
- refs->did_packed = 0;
+ if (refs->packed) {
+ free_ref_entry(refs->packed);
+ refs->packed = NULL;
+ }
}
static void clear_loose_ref_cache(struct ref_cache *refs)
{
- if (refs->did_loose)
- clear_ref_dir(&refs->loose);
- refs->did_loose = 0;
+ if (refs->loose) {
+ free_ref_entry(refs->loose);
+ refs->loose = NULL;
+ }
}
static struct ref_cache *create_ref_cache(const char *submodule)
@@ -725,22 +818,22 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
static struct ref_dir *get_packed_refs(struct ref_cache *refs)
{
- if (!refs->did_packed) {
+ if (!refs->packed) {
const char *packed_refs_file;
FILE *f;
+ refs->packed = create_dir_entry(refs, "", 0);
if (*refs->name)
packed_refs_file = git_path_submodule(refs->name, "packed-refs");
else
packed_refs_file = git_path("packed-refs");
f = fopen(packed_refs_file, "r");
if (f) {
- read_packed_refs(f, &refs->packed);
+ read_packed_refs(f, get_ref_dir(refs->packed));
fclose(f);
}
- refs->did_packed = 1;
}
- return &refs->packed;
+ return get_ref_dir(refs->packed);
}
void add_packed_ref(const char *refname, const unsigned char *sha1)
@@ -749,76 +842,89 @@ void add_packed_ref(const char *refname, const unsigned char *sha1)
create_ref_entry(refname, sha1, REF_ISPACKED, 1));
}
-static void get_ref_dir(struct ref_cache *refs, const char *base,
- struct ref_dir *dir)
+/*
+ * Read the loose references from the namespace dirname into dir
+ * (without recursing). dirname must end with '/'. dir must be the
+ * directory entry corresponding to dirname.
+ */
+static void read_loose_refs(const char *dirname, struct ref_dir *dir)
{
+ struct ref_cache *refs = dir->ref_cache;
DIR *d;
const char *path;
+ struct dirent *de;
+ int dirnamelen = strlen(dirname);
+ struct strbuf refname;
if (*refs->name)
- path = git_path_submodule(refs->name, "%s", base);
+ path = git_path_submodule(refs->name, "%s", dirname);
else
- path = git_path("%s", base);
+ path = git_path("%s", dirname);
d = opendir(path);
- if (d) {
- struct dirent *de;
- int baselen = strlen(base);
- char *refname = xmalloc(baselen + 257);
-
- memcpy(refname, base, baselen);
- if (baselen && base[baselen-1] != '/')
- refname[baselen++] = '/';
-
- while ((de = readdir(d)) != NULL) {
- unsigned char sha1[20];
- struct stat st;
- int flag;
- int namelen;
- const char *refdir;
-
- if (de->d_name[0] == '.')
- continue;
- namelen = strlen(de->d_name);
- if (namelen > 255)
- continue;
- if (has_extension(de->d_name, ".lock"))
- continue;
- memcpy(refname + baselen, de->d_name, namelen+1);
- refdir = *refs->name
- ? git_path_submodule(refs->name, "%s", refname)
- : git_path("%s", refname);
- if (stat(refdir, &st) < 0)
- continue;
- if (S_ISDIR(st.st_mode)) {
- get_ref_dir(refs, refname, dir);
- continue;
- }
+ if (!d)
+ return;
+
+ strbuf_init(&refname, dirnamelen + 257);
+ strbuf_add(&refname, dirname, dirnamelen);
+
+ while ((de = readdir(d)) != NULL) {
+ unsigned char sha1[20];
+ struct stat st;
+ int flag;
+ const char *refdir;
+
+ if (de->d_name[0] == '.')
+ continue;
+ if (has_extension(de->d_name, ".lock"))
+ continue;
+ strbuf_addstr(&refname, de->d_name);
+ refdir = *refs->name
+ ? git_path_submodule(refs->name, "%s", refname.buf)
+ : git_path("%s", refname.buf);
+ if (stat(refdir, &st) < 0) {
+ ; /* silently ignore */
+ } else if (S_ISDIR(st.st_mode)) {
+ strbuf_addch(&refname, '/');
+ add_entry_to_dir(dir,
+ create_dir_entry(refs, refname.buf, 1));
+ } else {
if (*refs->name) {
hashclr(sha1);
flag = 0;
- if (resolve_gitlink_ref(refs->name, refname, sha1) < 0) {
+ if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else if (read_ref_full(refname, sha1, 1, &flag)) {
+ } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- add_ref(dir, create_ref_entry(refname, sha1, flag, 1));
+ add_entry_to_dir(dir,
+ create_ref_entry(refname.buf, sha1, flag, 1));
}
- free(refname);
- closedir(d);
+ strbuf_setlen(&refname, dirnamelen);
}
+ strbuf_release(&refname);
+ closedir(d);
}
static struct ref_dir *get_loose_refs(struct ref_cache *refs)
{
- if (!refs->did_loose) {
- get_ref_dir(refs, "refs", &refs->loose);
- refs->did_loose = 1;
+ if (!refs->loose) {
+ /*
+ * Mark the top-level directory complete because we
+ * are about to read the only subdirectory that can
+ * hold references:
+ */
+ refs->loose = create_dir_entry(refs, "", 0);
+ /*
+ * Create an incomplete entry for "refs/":
+ */
+ add_entry_to_dir(get_ref_dir(refs->loose),
+ create_dir_entry(refs, "refs/", 1));
}
- return &refs->loose;
+ return get_ref_dir(refs->loose);
}
/* We allow "recursive" symbolic refs. Only within reason, though */
@@ -2224,57 +2330,59 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
return for_each_recent_reflog_ent(refname, fn, 0, cb_data);
}
-static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
+/*
+ * Call fn for each reflog in the namespace indicated by name. name
+ * must be empty or end with '/'. Name will be used as a scratch
+ * space, but its contents will be restored before return.
+ */
+static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
{
- DIR *d = opendir(git_path("logs/%s", base));
+ DIR *d = opendir(git_path("logs/%s", name->buf));
int retval = 0;
+ struct dirent *de;
+ int oldlen = name->len;
- if (d) {
- struct dirent *de;
- int baselen = strlen(base);
- char *log = xmalloc(baselen + 257);
+ if (!d)
+ return name->len ? errno : 0;
- memcpy(log, base, baselen);
- if (baselen && base[baselen-1] != '/')
- log[baselen++] = '/';
-
- while ((de = readdir(d)) != NULL) {
- struct stat st;
- int namelen;
+ while ((de = readdir(d)) != NULL) {
+ struct stat st;
- if (de->d_name[0] == '.')
- continue;
- namelen = strlen(de->d_name);
- if (namelen > 255)
- continue;
- if (has_extension(de->d_name, ".lock"))
- continue;
- memcpy(log + baselen, de->d_name, namelen+1);
- if (stat(git_path("logs/%s", log), &st) < 0)
- continue;
+ if (de->d_name[0] == '.')
+ continue;
+ if (has_extension(de->d_name, ".lock"))
+ continue;
+ strbuf_addstr(name, de->d_name);
+ if (stat(git_path("logs/%s", name->buf), &st) < 0) {
+ ; /* silently ignore */
+ } else {
if (S_ISDIR(st.st_mode)) {
- retval = do_for_each_reflog(log, fn, cb_data);
+ strbuf_addch(name, '/');
+ retval = do_for_each_reflog(name, fn, cb_data);
} else {
unsigned char sha1[20];
- if (read_ref_full(log, sha1, 0, NULL))
- retval = error("bad ref for %s", log);
+ if (read_ref_full(name->buf, sha1, 0, NULL))
+ retval = error("bad ref for %s", name->buf);
else
- retval = fn(log, sha1, 0, cb_data);
+ retval = fn(name->buf, sha1, 0, cb_data);
}
if (retval)
break;
}
- free(log);
- closedir(d);
+ strbuf_setlen(name, oldlen);
}
- else if (*base)
- return errno;
+ closedir(d);
return retval;
}
int for_each_reflog(each_ref_fn fn, void *cb_data)
{
- return do_for_each_reflog("", fn, cb_data);
+ int retval;
+ struct strbuf name;
+ strbuf_init(&name, PATH_MAX);
+ retval = do_for_each_reflog(&name, fn, cb_data);
+ strbuf_release(&name);
+ return retval;
}
int update_ref(const char *action, const char *refname,
diff --git a/streaming.c b/streaming.c
index 7e7ee2be6f..3a3cd1206a 100644
--- a/streaming.c
+++ b/streaming.c
@@ -99,7 +99,7 @@ int close_istream(struct git_istream *st)
return r;
}
-ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
+ssize_t read_istream(struct git_istream *st, void *buf, size_t sz)
{
return st->vtbl->read(st, buf, sz);
}
diff --git a/streaming.h b/streaming.h
index 3e827709c8..1d05c2a465 100644
--- a/streaming.h
+++ b/streaming.h
@@ -10,7 +10,7 @@ struct git_istream;
extern struct git_istream *open_istream(const unsigned char *, enum object_type *, unsigned long *, struct stream_filter *);
extern int close_istream(struct git_istream *);
-extern ssize_t read_istream(struct git_istream *, char *, size_t);
+extern ssize_t read_istream(struct git_istream *, void *, size_t);
extern int stream_blob_to_fd(int fd, const unsigned char *, struct stream_filter *, int can_seek);
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 4d127f19b7..55ed955cef 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -134,4 +134,16 @@ test_expect_success 'repack' '
git repack -ad
'
+test_expect_success 'tar achiving' '
+ git archive --format=tar HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, store only' '
+ git archive --format=zip -0 HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, deflate' '
+ git archive --format=zip HEAD >/dev/null
+'
+
test_done
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index caa687b5b4..9a105fe21f 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -65,20 +65,73 @@ test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
'
cat >expect <<'EOF'
-Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+HEAD@{Thu Apr 7 15:13:13 2005 -0700}
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (format=%gd)' '
+ git log -g -1 --format=%gd HEAD@{now} >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
Reflog message: commit (initial): one
EOF
test_expect_success 'using --date= shows reflog date (multiline)' '
- git log -g -1 --date=raw >tmp &&
+ git log -g -1 --date=default >tmp &&
grep ^Reflog <tmp >actual &&
test_cmp expect actual
'
cat >expect <<'EOF'
-e46513e HEAD@{1112911993 -0700}: commit (initial): one
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
EOF
test_expect_success 'using --date= shows reflog date (oneline)' '
- git log -g -1 --oneline --date=raw >actual &&
+ git log -g -1 --oneline --date=default >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{1112911993 -0700}
+EOF
+test_expect_success 'using --date= shows reflog date (format=%gd)' '
+ git log -g -1 --format=%gd --date=raw >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (multiline)' '
+ test_config log.date raw &&
+ git log -g -1 >tmp &&
+ grep ^Reflog <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (oneline)' '
+ test_config log.date raw &&
+ git log -g -1 --oneline >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (format=%gd)' '
+ test_config log.date raw &&
+ git log -g -1 --format=%gd >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success '--date magic does not override explicit @{0} syntax' '
+ git log -g -1 --format=%gd --date=raw HEAD@{0} >actual &&
test_cmp expect actual
'
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index 6352b74e2e..37bdcedcc9 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -46,4 +46,15 @@ test_expect_success 'checking out another branch from unborn state' '
test_cmp expect actual
'
+test_expect_success 'checking out in a newly created repo' '
+ test_create_repo empty &&
+ (
+ cd empty &&
+ git symbolic-ref HEAD >expect &&
+ test_must_fail git checkout &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index b37ce25c42..81005373d7 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -11,14 +11,13 @@ check_not_detached () {
git symbolic-ref -q HEAD >/dev/null
}
-ORPHAN_WARNING='you are leaving .* commit.*behind'
PREV_HEAD_DESC='Previous HEAD position was'
check_orphan_warning() {
- test_i18ngrep "$ORPHAN_WARNING" "$1" &&
+ test_i18ngrep "you are leaving $2 behind" "$1" &&
test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
}
check_no_orphan_warning() {
- test_i18ngrep ! "$ORPHAN_WARNING" "$1" &&
+ test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
test_i18ngrep "$PREV_HEAD_DESC" "$1"
}
@@ -110,12 +109,24 @@ test_expect_success 'checkout warns on orphan commits' '
git checkout --detach two &&
echo content >orphan &&
git add orphan &&
- git commit -a -m orphan &&
+ git commit -a -m orphan1 &&
+ echo new content >orphan &&
+ git commit -a -m orphan2 &&
+ orphan2=$(git rev-parse HEAD) &&
git checkout master 2>stderr
'
test_expect_success 'checkout warns on orphan commits: output' '
- check_orphan_warning stderr
+ check_orphan_warning stderr "2 commits"
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits' '
+ git checkout "$orphan2" &&
+ git checkout HEAD^ 2>stderr
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits: output' '
+ check_orphan_warning stderr "1 commit"
'
test_expect_success 'checkout does not warn leaving ref tip' '
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 527c9e7548..ecf00edab2 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -31,6 +31,26 @@ GUNZIP=${GUNZIP:-gzip -d}
SUBSTFORMAT=%H%n
+check_zip() {
+ zipfile=$1.zip
+ listfile=$1.lst
+ dir=$1
+ dir_with_prefix=$dir/$2
+
+ test_expect_success UNZIP " extract ZIP archive" "
+ (mkdir $dir && cd $dir && $UNZIP ../$zipfile)
+ "
+
+ test_expect_success UNZIP " validate filenames" "
+ (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+ test_cmp a.lst $listfile
+ "
+
+ test_expect_success UNZIP " validate file contents" "
+ diff -r a ${dir_with_prefix}a
+ "
+}
+
test_expect_success \
'populate workdir' \
'mkdir a b c &&
@@ -84,6 +104,12 @@ test_expect_success \
'git archive vs. git tar-tree' \
'test_cmp b.tar b2.tar'
+test_expect_success 'git archive on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive HEAD >b3.tar &&
+ test_cmp b.tar b3.tar
+'
+
test_expect_success \
'git archive in a bare repo' \
'(cd bare.git && git archive HEAD) >b3.tar'
@@ -175,10 +201,19 @@ test_expect_success \
test_cmp a/substfile2 g/prefix/a/substfile2
'
+$UNZIP -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+ say "Skipping ZIP tests, because unzip was not found"
+else
+ test_set_prereq UNZIP
+fi
+
test_expect_success \
'git archive --format=zip' \
'git archive --format=zip HEAD >d.zip'
+check_zip d
+
test_expect_success \
'git archive --format=zip in a bare repo' \
'(cd bare.git && git archive --format=zip HEAD) >d1.zip'
@@ -201,42 +236,25 @@ test_expect_success 'git archive with --output, override inferred format' '
test_cmp b.tar d4.zip
'
-$UNZIP -v >/dev/null 2>&1
-if [ $? -eq 127 ]; then
- say "Skipping ZIP tests, because unzip was not found"
-else
- test_set_prereq UNZIP
-fi
-
-test_expect_success UNZIP \
- 'extract ZIP archive' \
- '(mkdir d && cd d && $UNZIP ../d.zip)'
-
-test_expect_success UNZIP \
- 'validate filenames' \
- '(cd d/a && find .) | sort >d.lst &&
- test_cmp a.lst d.lst'
-
-test_expect_success UNZIP \
- 'validate file contents' \
- 'diff -r a d/a'
-
test_expect_success \
'git archive --format=zip with prefix' \
'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
-test_expect_success UNZIP \
- 'extract ZIP archive with prefix' \
- '(mkdir e && cd e && $UNZIP ../e.zip)'
+check_zip e prefix/
-test_expect_success UNZIP \
- 'validate filenames with prefix' \
- '(cd e/prefix/a && find .) | sort >e.lst &&
- test_cmp a.lst e.lst'
+test_expect_success 'git archive -0 --format=zip on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive -0 --format=zip HEAD >large.zip
+'
-test_expect_success UNZIP \
- 'validate file contents with prefix' \
- 'diff -r a e/prefix/a'
+check_zip large
+
+test_expect_success 'git archive --format=zip on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive --format=zip HEAD >large-compressed.zip
+'
+
+check_zip large-compressed
test_expect_success \
'git archive --list outside of a git repo' \
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 8f5cfac331..28e184829c 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -295,6 +295,15 @@ test_expect_success 'status -s -b' '
'
+test_expect_success 'status -s -z -b' '
+ tr "\\n" Q <expect >expect.q &&
+ mv expect.q expect &&
+ git status -s -z -b >output &&
+ nul_to_q <output >output.q &&
+ mv output.q output &&
+ test_cmp expect output
+'
+
test_expect_success 'setup dir3' '
mkdir dir3 &&
: >dir3/untracked1 &&
@@ -671,9 +680,14 @@ test_expect_success 'status --porcelain ignores color.status' '
git config --unset color.status
git config --unset color.ui
-test_expect_success 'status --porcelain ignores -b' '
+test_expect_success 'status --porcelain respects -b' '
git status --porcelain -b >output &&
+ {
+ echo "## master" &&
+ cat expect
+ } >tmp &&
+ mv tmp expect &&
test_cmp expect output
'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a0ea946327..c8f3ec6bec 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -51,7 +51,7 @@ run_completion ()
local _cword
_words=( $1 )
(( _cword = ${#_words[@]} - 1 ))
- _git && print_comp
+ __git_wrap_git && print_comp
}
test_completion ()
diff --git a/wt-status.c b/wt-status.c
index eeef17e7b6..dd6d8c4106 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -801,7 +801,7 @@ void wt_status_print(struct wt_status *s)
}
}
-static void wt_shortstatus_unmerged(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_unmerged(struct string_list_item *it,
struct wt_status *s)
{
struct wt_status_change_data *d = it->util;
@@ -817,7 +817,7 @@ static void wt_shortstatus_unmerged(int null_termination, struct string_list_ite
case 7: how = "UU"; break; /* both modified */
}
color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
- if (null_termination) {
+ if (s->null_termination) {
fprintf(stdout, " %s%c", it->string, 0);
} else {
struct strbuf onebuf = STRBUF_INIT;
@@ -828,7 +828,7 @@ static void wt_shortstatus_unmerged(int null_termination, struct string_list_ite
}
}
-static void wt_shortstatus_status(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_status(struct string_list_item *it,
struct wt_status *s)
{
struct wt_status_change_data *d = it->util;
@@ -842,7 +842,7 @@ static void wt_shortstatus_status(int null_termination, struct string_list_item
else
putchar(' ');
putchar(' ');
- if (null_termination) {
+ if (s->null_termination) {
fprintf(stdout, "%s%c", it->string, 0);
if (d->head_path)
fprintf(stdout, "%s%c", d->head_path, 0);
@@ -870,10 +870,10 @@ static void wt_shortstatus_status(int null_termination, struct string_list_item
}
}
-static void wt_shortstatus_other(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_other(struct string_list_item *it,
struct wt_status *s, const char *sign)
{
- if (null_termination) {
+ if (s->null_termination) {
fprintf(stdout, "%s %s%c", sign, it->string, 0);
} else {
struct strbuf onebuf = STRBUF_INIT;
@@ -913,8 +913,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
if (s->is_initial)
color_fprintf(s->fp, header_color, _("Initial commit on "));
if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
- color_fprintf_ln(s->fp, branch_color_local,
- "%s", branch_name);
+ color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+ fputc(s->null_termination ? '\0' : '\n', s->fp);
return;
}
@@ -938,14 +938,15 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
}
- color_fprintf_ln(s->fp, header_color, "]");
+ color_fprintf(s->fp, header_color, "]");
+ fputc(s->null_termination ? '\0' : '\n', s->fp);
}
-void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
+void wt_shortstatus_print(struct wt_status *s)
{
int i;
- if (show_branch)
+ if (s->show_branch)
wt_shortstatus_print_tracking(s);
for (i = 0; i < s->change.nr; i++) {
@@ -955,28 +956,28 @@ void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_br
it = &(s->change.items[i]);
d = it->util;
if (d->stagemask)
- wt_shortstatus_unmerged(null_termination, it, s);
+ wt_shortstatus_unmerged(it, s);
else
- wt_shortstatus_status(null_termination, it, s);
+ wt_shortstatus_status(it, s);
}
for (i = 0; i < s->untracked.nr; i++) {
struct string_list_item *it;
it = &(s->untracked.items[i]);
- wt_shortstatus_other(null_termination, it, s, "??");
+ wt_shortstatus_other(it, s, "??");
}
for (i = 0; i < s->ignored.nr; i++) {
struct string_list_item *it;
it = &(s->ignored.items[i]);
- wt_shortstatus_other(null_termination, it, s, "!!");
+ wt_shortstatus_other(it, s, "!!");
}
}
-void wt_porcelain_print(struct wt_status *s, int null_termination)
+void wt_porcelain_print(struct wt_status *s)
{
s->use_color = 0;
s->relative_paths = 0;
s->prefix = NULL;
- wt_shortstatus_print(s, null_termination, 0);
+ wt_shortstatus_print(s);
}
diff --git a/wt-status.h b/wt-status.h
index 6dd7207e25..14aa9f7e13 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -56,7 +56,9 @@ struct wt_status {
enum untracked_status_type show_untracked_files;
const char *ignore_submodule_arg;
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
- int colopts;
+ unsigned colopts;
+ int null_termination;
+ int show_branch;
/* These are computed during processing of the individual sections */
int commitable;
@@ -73,8 +75,8 @@ void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
void wt_status_collect(struct wt_status *s);
-void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
-void wt_porcelain_print(struct wt_status *s, int null_termination);
+void wt_shortstatus_print(struct wt_status *s);
+void wt_porcelain_print(struct wt_status *s);
void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
;