Merge branch 'jk/status-porcelain-z-b'
"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.
By Jeff King
via Jeff King
* jk/status-porcelain-z-b:
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
diff --git a/Documentation/RelNotes/1.7.10.2.txt b/Documentation/RelNotes/1.7.10.2.txt
index 04fe294..55e960a 100644
--- a/Documentation/RelNotes/1.7.10.2.txt
+++ b/Documentation/RelNotes/1.7.10.2.txt
@@ -24,6 +24,9 @@
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 +36,33 @@
* 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 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.
+
+ * A contrib script "rerere-train" did not work out of the box unless
+ user futzed with her $PATH.
+
* 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/git-config.txt b/Documentation/git-config.txt
index 81b0398..3f5d216 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -44,11 +44,15 @@
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/Makefile b/Makefile
index 2fa7211..3eda1e8 100644
--- a/Makefile
+++ b/Makefile
@@ -247,6 +247,9 @@
# 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 @@
export ASCIIDOC7
endif
+ifdef NO_INSTALL_HARDLINKS
+ export NO_INSTALL_HARDLINKS
+endif
+
### profile feedback build
#
@@ -2574,19 +2581,21 @@
{ 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 @@
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 20af005..93387ea 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 @@
* 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 @@
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 @@
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 @@
}
/*
+ * 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 @@
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;
- } else {
- 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);
+ 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 {
- 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 {
+ sprintf(header.name, "%s.data",
+ sha1_to_hex(sha1));
+ strbuf_append_ext_header(&ext_header, "path",
+ 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 @@
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 @@
{
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 02d1f37..f5af81f 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_entries;
#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+#define ZIP_STREAM (8)
struct zip_local_header {
unsigned char magic[4];
@@ -31,6 +33,14 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 1ee837d..cd083ea 100644
--- a/archive.c
+++ b/archive.c
@@ -59,12 +59,15 @@
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 @@
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 @@
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 @@
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 2b0884f..895afcd 100644
--- a/archive.h
+++ b/archive.h
@@ -11,6 +11,7 @@
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 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 6e186e2..48acf73 100644
--- a/bisect.c
+++ b/bisect.c
@@ -833,7 +833,7 @@
*/
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 @@
/* 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 @@
filename, strerror(errno));
else
close(fd);
+ done:
+ free(filename);
}
/*
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 23fc56d..c93efe4 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -672,10 +672,10 @@
* 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 @@
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 @@
}
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);
diff --git a/builtin/clone.c b/builtin/clone.c
index bbd5c96..a4d8d25 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -569,7 +569,7 @@
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/grep.c b/grep.c
index 190139c..f8ffa46 100644
--- a/grep.c
+++ b/grep.c
@@ -318,7 +318,7 @@
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/refs.c b/refs.c
index a5802e1..d6bdb47 100644
--- a/refs.c
+++ b/refs.c
@@ -101,11 +101,45 @@
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 @@
*/
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 @@
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 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 @@
* 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 @@
/*
* 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 @@
}
/*
+ * 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 @@
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 @@
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 @@
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 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 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 @@
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);
+ if (!d)
+ return;
- memcpy(refname, base, baselen);
- if (baselen && base[baselen-1] != '/')
- refname[baselen++] = '/';
+ strbuf_init(&refname, dirnamelen + 257);
+ strbuf_add(&refname, dirname, dirnamelen);
- while ((de = readdir(d)) != NULL) {
- unsigned char sha1[20];
- struct stat st;
- int flag;
- int namelen;
- const char *refdir;
+ while ((de = readdir(d)) != NULL) {
+ unsigned char sha1[20];
+ struct stat st;
+ int flag;
+ 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 (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 @@
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;
- while ((de = readdir(d)) != NULL) {
- struct stat st;
- int namelen;
-
- 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 7e7ee2b..3a3cd12 100644
--- a/streaming.c
+++ b/streaming.c
@@ -99,7 +99,7 @@
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 3e82770..1d05c2a 100644
--- a/streaming.h
+++ b/streaming.h
@@ -10,7 +10,7 @@
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 4d127f1..55ed955 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -134,4 +134,16 @@
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/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index b37ce25..8100537 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -11,14 +11,13 @@
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 @@
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 527c9e7..ecf00ed 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -31,6 +31,26 @@
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 @@
'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_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_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' \