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 ...
This commit is contained in:
commit
423b5a1044
@ -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
|
||||
|
@ -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).
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
-------------
|
||||
|
@ -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
|
||||
|
@ -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].
|
||||
|
||||
|
@ -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
|
||||
|
12
Makefile
12
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; \
|
||||
|
153
archive-tar.c
153
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).
|
||||
@ -77,6 +88,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
|
||||
@ -123,25 +161,57 @@ 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));
|
||||
} else {
|
||||
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
||||
*header.typeflag = TYPEFLAG_DIR;
|
||||
mode = (mode | 0777) & ~tar_umask;
|
||||
@ -170,9 +240,22 @@ static int write_tar_entry(struct archiver_args *args,
|
||||
}
|
||||
} 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,
|
||||
err = write_extended_header(args, sha1, ext_header.buf,
|
||||
ext_header.len);
|
||||
if (err)
|
||||
if (err) {
|
||||
free(buffer);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
strbuf_release(&ext_header);
|
||||
write_blocked(&header, sizeof(header));
|
||||
if (S_ISREG(mode) && buffer && size > 0)
|
||||
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;
|
||||
}
|
||||
|
198
archive-zip.c
198
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;
|
||||
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;
|
||||
uncompressed_size = size;
|
||||
compressed_size = size;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
26
archive.c
26
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,
|
||||
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, const struct commit *commit)
|
||||
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;
|
||||
}
|
||||
|
10
archive.h
10
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 */
|
||||
|
8
bisect.c
8
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
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, _("ahead %d, behind %d] "), ours, theirs);
|
||||
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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
144
builtin/commit.c
144
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);
|
||||
|
@ -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) {
|
||||
|
1
commit.h
1
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;
|
||||
|
@ -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
|
||||
|
76
dir.c
76
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)
|
||||
|
2
grep.c
2
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.");
|
||||
|
@ -630,9 +630,8 @@ void show_log(struct rev_info *opt)
|
||||
*/
|
||||
show_reflog_message(opt->reflog_info,
|
||||
opt->commit_format == CMIT_FMT_ONELINE,
|
||||
opt->date_mode_explicit ?
|
||||
opt->date_mode :
|
||||
DATE_NORMAL);
|
||||
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;
|
||||
|
1
pretty.c
1
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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
332
refs.c
332
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)
|
||||
{
|
||||
@ -276,39 +343,61 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname
|
||||
return *r;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
entry = create_dir_entry(refname_copy);
|
||||
add_entry_to_dir(dir, entry);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
|
||||
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);
|
||||
strbuf_addstr(&refname, de->d_name);
|
||||
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;
|
||||
}
|
||||
? 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);
|
||||
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;
|
||||
|
||||
if (d) {
|
||||
struct dirent *de;
|
||||
int baselen = strlen(base);
|
||||
char *log = xmalloc(baselen + 257);
|
||||
int oldlen = name->len;
|
||||
|
||||
memcpy(log, base, baselen);
|
||||
if (baselen && base[baselen-1] != '/')
|
||||
log[baselen++] = '/';
|
||||
if (!d)
|
||||
return name->len ? errno : 0;
|
||||
|
||||
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;
|
||||
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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
'
|
||||
|
||||
|
@ -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
|
||||
|
@ -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' '
|
||||
|
@ -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' \
|
||||
|
@ -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
|
||||
|
||||
'
|
||||
|
@ -51,7 +51,7 @@ run_completion ()
|
||||
local _cword
|
||||
_words=( $1 )
|
||||
(( _cword = ${#_words[@]} - 1 ))
|
||||
_git && print_comp
|
||||
__git_wrap_git && print_comp
|
||||
}
|
||||
|
||||
test_completion ()
|
||||
|
35
wt-status.c
35
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);
|
||||
}
|
||||
|
@ -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, ...)
|
||||
;
|
||||
|
Loading…
Reference in New Issue
Block a user