Doc update.
* jw/gitweb-sample-update:
doc: don't use git.kernel.org as example gitweb URL
Cpp11BracedListStyle: false
# A list of macros that should be interpreted as foreach loops instead of as
-# function calls.
-ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut']
+# function calls. Taken from:
+# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
+# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
+# | sort | uniq
+ForEachMacros:
+ - 'for_each_abbrev'
+ - 'for_each_builtin'
+ - 'for_each_string_list_item'
+ - 'for_each_ut'
+ - 'for_each_wanted_builtin'
+ - 'list_for_each'
+ - 'list_for_each_dir'
+ - 'list_for_each_prev'
+ - 'list_for_each_prev_safe'
+ - 'list_for_each_safe'
# The maximum number of consecutive empty lines to keep.
MaxEmptyLinesToKeep: 1
must be declared with "extern" in header files. However, function
declarations should not use "extern", as that is already the default.
+ - You can launch gdb around your program using the shorthand GIT_DEBUGGER.
+ Run `GIT_DEBUGGER=1 ./bin-wrappers/git foo` to simply use gdb as is, or
+ run `GIT_DEBUGGER="<debugger> <debugger-args>" ./bin-wrappers/git foo` to
+ use your own debugger and arguments. Example: `GIT_DEBUGGER="ddd --gdb"
+ ./bin-wrappers/git log` (See `wrap-for-bin.sh`.)
+
For Perl programs:
- Most of the C guidelines above apply.
--- /dev/null
+Git 2.22.1 Release Notes
+========================
+
+Fixes since v2.22
+-----------------
+
+ * A relative pathname given to "git init --template=<path> <repo>"
+ ought to be relative to the directory "git init" gets invoked in,
+ but it instead was made relative to the repository, which has been
+ corrected.
+
+ * "git worktree add" used to fail when another worktree connected to
+ the same repository was corrupt, which has been corrected.
+
+ * The ownership rule for the file descriptor to fast-import remote
+ backend was mixed up, leading to unrelated file descriptor getting
+ closed, which has been fixed.
+
+ * "git update-server-info" used to leave stale packfiles in its
+ output, which has been corrected.
+
+ * The server side support for "git fetch" used to show incorrect
+ value for the HEAD symbolic ref when the namespace feature is in
+ use, which has been corrected.
+
+ * "git am -i --resolved" segfaulted after trying to see a commit as
+ if it were a tree, which has been corrected.
+
+ * "git bundle verify" needs to see if prerequisite objects exist in
+ the receiving repository, but the command did not check if we are
+ in a repository upfront, which has been corrected.
+
+ * "git merge --squash" is designed to update the working tree and the
+ index without creating the commit, and this cannot be countermanded
+ by adding the "--commit" option; the command now refuses to work
+ when both options are given.
+
+ * The data collected by fsmonitor was not properly written back to
+ the on-disk index file, breaking t7519 tests occasionally, which
+ has been corrected.
+
+ * Update to Unicode 12.1 width table.
+
+ * The command line to invoke a "git cat-file" command from inside
+ "git p4" was not properly quoted to protect a caret and running a
+ broken command on Windows, which has been corrected.
+
+ * "git request-pull" learned to warn when the ref we ask them to pull
+ from in the local repository and in the published repository are
+ different.
+
+ * When creating a partial clone, the object filtering criteria is
+ recorded for the origin of the clone, but this incorrectly used a
+ hardcoded name "origin" to name that remote; it has been corrected
+ to honor the "--origin <name>" option.
+
+ * "git fetch" into a lazy clone forgot to fetch base objects that are
+ necessary to complete delta in a thin packfile, which has been
+ corrected.
+
+ * The filter_data used in the list-objects-filter (which manages a
+ lazily sparse clone repository) did not use the dynamic array API
+ correctly---'nr' is supposed to point at one past the last element
+ of the array in use. This has been corrected.
+
+ * The description about slashes in gitignore patterns (used to
+ indicate things like "anchored to this level only" and "only
+ matches directories") has been revamped.
+
+ * The URL decoding code has been updated to avoid going past the end
+ of the string while parsing %-<hex>-<hex> sequence.
+
+ * The list of for-each like macros used by clang-format has been
+ updated.
+
+Also contains various documentation updates and code clean-ups.
Use this custom program instead of "`gpg`" found on `$PATH` when
making or verifying a PGP signature. The program must support the
same command-line interface as GPG, namely, to verify a detached
- signature, "`gpg --verify $file - <$signature`" is run, and the
+ signature, "`gpg --verify $signature - <$file`" is run, and the
program is expected to signal a good signature by exiting with
code 0, and to generate an ASCII-armored detached signature, the
standard input of "`gpg -bsau $key`" is fed with the contents to be
with the contents of the named file (which can be outside of the
work tree), and optionally writes the resulting object into the
object database. Reports its object ID to its standard output.
-This is used by 'git cvsimport' to update the index
-without modifying files in the work tree. When <type> is not
-specified, it defaults to "blob".
+When <type> is not specified, it defaults to "blob".
OPTIONS
-------
helper configured (see linkgit:git-credential[1]), the password will be saved in
the credential store so you won't have to type it the next time.
-Note: the following perl modules are required
- Net::SMTP::SSL, MIME::Base64 and Authen::SASL
+Note: the following core Perl modules that may be installed with your
+distribution of Perl are required:
+MIME::Base64, MIME::QuotedPrint, Net::Domain and Net::SMTP.
+These additional Perl modules are also required:
+Authen::SASL and Mail::Address.
+
SEE ALSO
--------
Run as if git was started in '<path>' instead of the current working
directory. When multiple `-C` options are given, each subsequent
non-absolute `-C <path>` is interpreted relative to the preceding `-C
- <path>`.
+ <path>`. If '<path>' is present but empty, e.g. `-C ""`, then the
+ current working directory is left unchanged.
+
This option affects options that expect path name like `--git-dir` and
`--work-tree` in that their interpretations of the path names would be
Put a backslash ("`\`") in front of the first "`!`" for patterns
that begin with a literal "`!`", for example, "`\!important!.txt`".
- - If the pattern ends with a slash, it is removed for the
- purpose of the following description, but it would only find
- a match with a directory. In other words, `foo/` will match a
- directory `foo` and paths underneath it, but will not match a
- regular file or a symbolic link `foo` (this is consistent
- with the way how pathspec works in general in Git).
-
- - If the pattern does not contain a slash '/', Git treats it as
- a shell glob pattern and checks for a match against the
- pathname relative to the location of the `.gitignore` file
- (relative to the toplevel of the work tree if not from a
- `.gitignore` file).
-
- - Otherwise, Git treats the pattern as a shell glob: "`*`" matches
- anything except "`/`", "`?`" matches any one character except "`/`"
- and "`[]`" matches one character in a selected range. See
- fnmatch(3) and the FNM_PATHNAME flag for a more detailed
- description.
-
- - A leading slash matches the beginning of the pathname.
- For example, "/{asterisk}.c" matches "cat-file.c" but not
- "mozilla-sha1/sha1.c".
+ - The slash '/' is used as the directory separator. Separators may
+ occur at the beginning, middle or end of the `.gitignore` search pattern.
+
+ - If there is a separator at the beginning or middle (or both) of the
+ pattern, then the pattern is relative to the directory level of the
+ particular `.gitignore` file itself. Otherwise the pattern may also
+ match at any level below the `.gitignore` level.
+
+ - If there is a separator at the end of the pattern then the pattern
+ will only match directories, otherwise the pattern can match both
+ files and directories.
+
+ - For example, a pattern `doc/frotz/` matches `doc/frotz` directory,
+ but not `a/doc/frotz` directory; however `frotz/` matches `frotz`
+ and `a/frotz` that is a directory (all paths are relative from
+ the `.gitignore` file).
+
+ - An asterisk "`*`" matches anything except a slash.
+ The character "`?`" matches any one character except "`/`".
+ The range notation, e.g. `[a-zA-Z]`, can be used to match
+ one of the characters in a range. See fnmatch(3) and the
+ FNM_PATHNAME flag for a more detailed description.
Two consecutive asterisks ("`**`") in patterns matched against
full pathname may have special meaning:
EXAMPLES
--------
+ - The pattern `hello.*` matches any file or folder
+ whose name begins with `hello`. If one wants to restrict
+ this only to the directory and not in its subdirectories,
+ one can prepend the pattern with a slash, i.e. `/hello.*`;
+ the pattern now matches `hello.txt`, `hello.c` but not
+ `a/hello.java`.
+
+ - The pattern `foo/` will match a directory `foo` and
+ paths underneath it, but will not match a regular file
+ or a symbolic link `foo` (this is consistent with the
+ way how pathspec works in general in Git)
+
+ - The pattern `doc/frotz` and `/doc/frotz` have the same effect
+ in any `.gitignore` file. In other words, a leading slash
+ is not relevant if there is already a middle slash in
+ the pattern.
+
+ - The pattern "foo/*", matches "foo/test.json"
+ (a regular file), "foo/bar" (a directory), but it does not match
+ "foo/bar/hello.c" (a regular file), as the asterisk in the
+ pattern does not match "bar/hello.c" which has a slash in it.
+
--------------------------------------------------------------
$ git status
[...]
+
With --no-squash perform the merge and commit the result. This
option can be used to override --squash.
++
+With --squash, --commit is not allowed, and will fail.
-s <strategy>::
--strategy=<strategy>::
"event":"signal",
...
"t_abs":0.001227, # elapsed time in seconds
- "signal":13 # SIGTERM, SIGINT, etc.
+ "signo":13 # SIGTERM, SIGINT, etc.
}
------------
See [Documentation/gittutorial.txt][] to get started, then see
[Documentation/giteveryday.txt][] for a useful minimum set of commands, and
-Documentation/git-<commandname>.txt for documentation of each command.
+`Documentation/git-<commandname>.txt` for documentation of each command.
If git has been correctly installed, then the tutorial can also be
read with `man gittutorial` or `git help tutorial`, and the
documentation of each command with `man git-<commandname>` or `git help
-Documentation/RelNotes/2.22.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.22.1.txt
\ No newline at end of file
"created file '%s'"),
path);
}
- fill_stat_cache_info(ce, &st);
+ fill_stat_cache_info(state->repo->index, ce, &st);
}
if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
discard_cache_entry(ce);
struct rev_info rev_info;
FILE *fp;
- if (!get_oid_tree("HEAD", &head))
- tree = lookup_tree(the_repository, &head);
- else
+ if (!get_oid("HEAD", &head)) {
+ struct commit *commit = lookup_commit_or_die(&head, "HEAD");
+ tree = get_commit_tree(commit);
+ } else
tree = lookup_tree(the_repository,
the_repository->hash_algo->empty_tree);
{
assert(state->msg);
- if (!isatty(0))
- die(_("cannot be interactive without stdin connected to a terminal."));
-
for (;;) {
- const char *reply;
+ char reply[64];
puts(_("Commit Body is:"));
puts("--------------------------");
* in your translation. The program will only accept English
* input at this point.
*/
- reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
+ printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "));
+ if (!fgets(reply, sizeof(reply), stdin))
+ die("unable to read from stdin; aborting");
- if (!reply) {
- continue;
- } else if (*reply == 'y' || *reply == 'Y') {
+ if (*reply == 'y' || *reply == 'Y') {
return 0;
} else if (*reply == 'a' || *reply == 'A') {
state->interactive = 0;
argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
}
+ if (state.interactive && !paths.argc)
+ die(_("interactive mode requires patches on the command line"));
+
am_setup(&state, patch_format, paths.argv, keep_cr);
argv_array_clear(&paths);
write_file(git_path_bisect_start(), "%s\n", start_head.buf);
if (no_checkout) {
- get_oid(start_head.buf, &oid);
+ if (get_oid(start_head.buf, &oid) < 0) {
+ retval = error(_("invalid ref: '%s'"), start_head.buf);
+ goto finish;
+ }
if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
UPDATE_REFS_MSG_ON_ERR)) {
retval = -1;
remote_head_points_at, &branch_top);
if (filter_options.choice)
- partial_clone_register("origin", &filter_options);
+ partial_clone_register(option_origin, &filter_options);
if (is_local)
clone_local(path, git_dir);
#include "thread-utils.h"
#include "packfile.h"
#include "object-store.h"
+#include "fetch-object.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
sorted_by_pos[i] = &ref_deltas[i];
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
+ if (repository_format_partial_clone) {
+ /*
+ * Prefetch the delta bases.
+ */
+ struct oid_array to_fetch = OID_ARRAY_INIT;
+ for (i = 0; i < nr_ref_deltas; i++) {
+ struct ref_delta_entry *d = sorted_by_pos[i];
+ if (!oid_object_info_extended(the_repository, &d->oid,
+ NULL,
+ OBJECT_INFO_FOR_PREFETCH))
+ continue;
+ oid_array_append(&to_fetch, &d->oid);
+ }
+ if (to_fetch.nr)
+ fetch_objects(repository_format_partial_clone,
+ to_fetch.oid, to_fetch.nr);
+ oid_array_clear(&to_fetch);
+ }
+
for (i = 0; i < nr_ref_deltas; i++) {
struct ref_delta_entry *d = sorted_by_pos[i];
enum object_type type;
int report_end_of_input = 0;
/*
- * index-pack never needs to fetch missing objects, since it only
- * accesses the repo to do hash collision checks
+ * index-pack never needs to fetch missing objects except when
+ * REF_DELTA bases are missing (which are explicitly handled). It only
+ * accesses the repo to do hash collision checks and to check which
+ * REF_DELTA bases need to be fetched.
*/
fetch_if_missing = 0;
if (real_git_dir && !is_absolute_path(real_git_dir))
real_git_dir = real_pathdup(real_git_dir, 1);
+ if (template_dir && *template_dir && !is_absolute_path(template_dir))
+ template_dir = absolute_pathdup(template_dir);
+
if (argc == 1) {
int mkdir_tried = 0;
retry:
};
static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1;
+static int option_commit = -1;
static int option_edit = -1;
static int allow_trivial = 1, have_message, verify_signatures;
static int overwrite_ignore = 1;
if (squash) {
if (fast_forward == FF_NO)
die(_("You cannot combine --squash with --no-ff."));
+ if (option_commit > 0)
+ die(_("You cannot combine --squash with --commit."));
+ /*
+ * squash can now silently disable option_commit - this is not
+ * a problem as it is only overriding the default, not a user
+ * supplied option.
+ */
option_commit = 0;
}
+ if (option_commit < 0)
+ option_commit = 1;
+
if (!argc) {
if (default_to_upstream)
argc = setup_with_upstream(&argv);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
- fill_stat_cache_info(ce, st);
+ fill_stat_cache_info(&the_index, ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
if (index_path(&the_index, &ce->oid, path, st,
int i, ret = 0, req_nr;
const char *message = _("Repository lacks these prerequisite commits:");
+ if (!r || !r->objects || !r->objects->odb)
+ return error(_("need a repository to verify a bundle"));
+
repo_init_revisions(r, &revs, NULL);
for (i = 0; i < p->nr; i++) {
struct ref_list_entry *e = p->list + i;
int match_stat_data_racy(const struct index_state *istate,
const struct stat_data *sd, struct stat *st);
-void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
popd
;;
osx-clang|osx-gcc)
- brew update >/dev/null
+ export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
# Uncomment this if you want to run perf tests:
# brew install gnu-time
test -z "$BREW_INSTALL_PACKAGES" ||
export GIT_TEST_HTTPD=YesPlease
# The Linux build installs the defined dependency versions below.
- # The OS X build installs the latest available versions. Keep that
- # in mind when you encounter a broken OS X build!
+ # The OS X build installs much more recent versions, whichever
+ # were recorded in the Homebrew database upon creating the OS X
+ # image.
+ # Keep that in mind when you encounter a broken OS X build!
export LINUX_P4_VERSION="16.2"
export LINUX_GIT_LFS_VERSION="1.5.2"
case FILE_TYPE_PIPE:
if (!once_only)
{
- NtQueryInformationFile = (PNtQueryInformationFile)
+ NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
GetProcAddress (GetModuleHandle ("ntdll.dll"),
"NtQueryInformationFile");
once_only = TRUE;
#include <wingdi.h>
#include <winreg.h>
#include "win32.h"
+#include "win32/lazyload.h"
static int fd_is_interactive[3] = { 0, 0, 0 };
#define FD_CONSOLE 0x1
#endif
#endif
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
- PCONSOLE_FONT_INFOEX);
-
static void warn_if_raster_font(void)
{
DWORD fontFamily = 0;
- PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+ DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+ HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
/* don't bother if output was ascii only */
if (!non_ascii_used)
return;
/* GetCurrentConsoleFontEx is available since Vista */
- pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
- GetModuleHandle("kernel32.dll"),
- "GetCurrentConsoleFontEx");
- if (pGetCurrentConsoleFontEx) {
+ if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
- if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+ if (GetCurrentConsoleFontEx(console, 0, &cfi))
fontFamily = cfi.FontFamily;
} else {
/* pre-Vista: check default console font in registry */
errno = EINVAL;
return 0;
}
- uval = labs(val);
+ uval = val < 0 ? -val : val;
uval *= factor;
- if (uval > max || labs(val) > uval) {
+ if (uval > max || (val < 0 ? -val : val) > uval) {
errno = ERANGE;
return 0;
}
if (!changed && !dirty_submodule) {
ce_mark_uptodate(ce);
- mark_fsmonitor_valid(ce);
+ mark_fsmonitor_valid(istate, ce);
if (!revs->diffopt.flags.find_copies_harder)
continue;
}
argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+ diff_free_filespec_data(one);
+ diff_free_filespec_data(two);
if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
die(_("external diff died, stopping at %s"), name);
if (lstat(ce->name, &st) < 0)
return error_errno("unable to stat just-written file %s",
ce->name);
- fill_stat_cache_info(ce, &st);
+ fill_stat_cache_info(state->istate, ce, &st);
ce->ce_flags |= CE_UPDATE_IN_BASE;
mark_fsmonitor_invalid(state->istate, ce);
state->istate->cache_changed |= CE_ENTRY_CHANGED;
* called any time the cache entry has been updated to reflect the
* current state of the file on disk.
*/
-static inline void mark_fsmonitor_valid(struct cache_entry *ce)
+static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache_entry *ce)
{
- if (core_fsmonitor) {
+ if (core_fsmonitor && !(ce->ce_flags & CE_FSMONITOR_VALID)) {
+ istate->cache_changed = 1;
ce->ce_flags |= CE_FSMONITOR_VALID;
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
}
## fixme: title is first line of commit, not 1st paragraph.
foundTitle = False
- for log in read_pipe_lines("git cat-file commit %s" % commit):
+ for log in read_pipe_lines(["git", "cat-file", "commit", commit]):
if not foundTitle:
if len(log) == 1:
foundTitle = True
class Command:
delete_actions = ( "delete", "move/delete", "purge" )
- add_actions = ( "add", "move/add" )
+ add_actions = ( "add", "branch", "move/add" )
def __init__(self):
self.usage = "usage: %prog [options]"
headrev=$(git rev-parse --verify --quiet "$head"^0)
test -z "$headrev" && die "fatal: Ambiguous revision: $local"
+local_sha1=$(git rev-parse --verify --quiet "$head")
+
# Was it a branch with a description?
branch_name=${head#refs/heads/}
if test "z$branch_name" = "z$headref" ||
die "fatal: No commits in common between $base and $head"
# $head is the refname from the command line.
-# If a ref with the same name as $head exists at the remote
-# and their values match, use that.
-#
-# Otherwise find a random ref that matches $headrev.
+# Find a ref with the same name as $head that exists at the remote
+# and points to the same commit as the local object.
find_matching_ref='
my ($head,$headrev) = (@ARGV);
- my ($found);
+ my $pattern = qr{/\Q$head\E$};
+ my ($remote_sha1, $found);
while (<STDIN>) {
chomp;
my ($sha1, $ref, $deref) = /^(\S+)\s+([^^]+)(\S*)$/;
- my ($pattern);
- next unless ($sha1 eq $headrev);
- $pattern="/$head\$";
- if ($ref eq $head) {
- $found = $ref;
- }
- if ($ref =~ /$pattern/) {
- $found = $ref;
- }
if ($sha1 eq $head) {
- $found = $sha1;
+ $found = $remote_sha1 = $sha1;
+ break;
+ }
+
+ if ($ref eq $head || $ref =~ $pattern) {
+ if ($deref eq "") {
+ # Remember the matching object on the remote side
+ $remote_sha1 = $sha1;
+ }
+ if ($sha1 eq $headrev) {
+ $found = $ref;
+ break;
+ }
}
}
if ($found) {
- print "$found\n";
+ $remote_sha1 = $headrev if ! defined $remote_sha1;
+ print "$remote_sha1 $found\n";
}
'
-ref=$(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+set fnord $(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+remote_sha1=$2
+ref=$3
if test -z "$ref"
then
echo "warn: No match for commit $headrev found at $url" >&2
echo "warn: Are you sure you pushed '${remote:-HEAD}' there?" >&2
status=1
+elif test "$local_sha1" != "$remote_sha1"
+then
+ echo "warn: $head found at $url but points to a different object" >&2
+ echo "warn: Are you sure you pushed '${remote:-HEAD}' there?" >&2
+ status=1
fi
# Special case: turn "for_linus" to "tags/for_linus" when it is correct
enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg;
+ if (!opt->status_only && gs->name == NULL)
+ BUG("grep call which could print a name requires "
+ "grep_source.name be non-NULL");
+
if (!opt->output)
opt->output = std_output;
#include "compat/obstack.h"
#define NCHAR (UCHAR_MAX + 1)
-#define obstack_chunk_alloc xmalloc
+/* adapter for `xmalloc()`, which takes `size_t`, not `long` */
+static void *obstack_chunk_alloc(long size)
+{
+ if (size < 0)
+ BUG("Cannot allocate a negative amount: %ld", size);
+ return xmalloc(size);
+}
#define obstack_chunk_free free
#define U(c) ((unsigned char) (c))
filename, &dtype, &filter_data->el,
r->index);
if (val < 0)
- val = filter_data->array_frame[filter_data->nr].defval;
+ val = filter_data->array_frame[filter_data->nr - 1].defval;
ALLOC_GROW(filter_data->array_frame, filter_data->nr + 1,
filter_data->alloc);
- filter_data->nr++;
filter_data->array_frame[filter_data->nr].defval = val;
filter_data->array_frame[filter_data->nr].child_prov_omit = 0;
+ filter_data->nr++;
/*
* A directory with this tree OID may appear in multiple
case LOFS_END_TREE:
assert(obj->type == OBJ_TREE);
- assert(filter_data->nr > 0);
+ assert(filter_data->nr > 1);
- frame = &filter_data->array_frame[filter_data->nr];
- filter_data->nr--;
+ frame = &filter_data->array_frame[--filter_data->nr];
/*
* Tell our parent directory if any of our children were
* provisionally omitted.
*/
- filter_data->array_frame[filter_data->nr].child_prov_omit |=
+ filter_data->array_frame[filter_data->nr - 1].child_prov_omit |=
frame->child_prov_omit;
/*
assert(obj->type == OBJ_BLOB);
assert((obj->flags & SEEN) == 0);
- frame = &filter_data->array_frame[filter_data->nr];
+ frame = &filter_data->array_frame[filter_data->nr - 1];
dtype = DT_REG;
val = is_excluded_from_list(pathname, strlen(pathname),
static void filter_sparse_free(void *filter_data)
{
struct filter_sparse_data *d = filter_data;
- /* TODO free contents of 'd' */
+ free(d->array_frame);
free(d);
}
ALLOC_GROW(d->array_frame, d->nr + 1, d->alloc);
d->array_frame[d->nr].defval = 0; /* default to include */
d->array_frame[d->nr].child_prov_omit = 0;
+ d->nr++;
*filter_fn = filter_sparse;
*filter_free_fn = filter_sparse_free;
if (!symref_target)
die("'%s' is a symref but it is not?", refname);
- strbuf_addf(&refline, " symref-target:%s", symref_target);
+ strbuf_addf(&refline, " symref-target:%s",
+ strip_namespace(symref_target));
}
if (data->peel) {
#define OBJECT_INFO_IGNORE_LOOSE 16
/*
* Do not attempt to fetch the object if missing (even if fetch_is_missing is
- * nonzero). This is meant for bulk prefetching of missing blobs in a partial
- * clone. Implies OBJECT_INFO_QUICK.
+ * nonzero).
*/
-#define OBJECT_INFO_FOR_PREFETCH (32 + OBJECT_INFO_QUICK)
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 32
+/*
+ * This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
+ */
+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
int oid_object_info_extended(struct repository *r,
const struct object_id *,
while (packed_git_limit < pack_mapped
&& unuse_one_window(p))
; /* nothing */
- win->base = xmmap(NULL, win->len,
+ win->base = xmmap_gently(NULL, win->len,
PROT_READ, MAP_PRIVATE,
p->pack_fd, win->offset);
if (win->base == MAP_FAILED)
if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))
continue;
ce_mark_uptodate(ce);
- mark_fsmonitor_valid(ce);
+ mark_fsmonitor_valid(index, ce);
} while (--nr > 0);
if (p->progress) {
struct progress_data *pd = p->progress;
* cache, ie the parts that aren't tracked by GIT, and only used
* to validate the cache.
*/
-void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st)
{
fill_stat_data(&ce->ce_stat_data, st);
if (S_ISREG(st->st_mode)) {
ce_mark_uptodate(ce);
- mark_fsmonitor_valid(ce);
+ mark_fsmonitor_valid(istate, ce);
}
}
memcpy(ce->name, path, namelen);
ce->ce_namelen = namelen;
if (!intent_only)
- fill_stat_cache_info(ce, st);
+ fill_stat_cache_info(istate, ce, st);
else
ce->ce_flags |= CE_INTENT_TO_ADD;
*/
if (!S_ISGITLINK(ce->ce_mode)) {
ce_mark_uptodate(ce);
- mark_fsmonitor_valid(ce);
+ mark_fsmonitor_valid(istate, ce);
}
return ce;
}
updated = make_empty_cache_entry(istate, ce_namelen(ce));
copy_cache_entry(updated, ce);
memcpy(updated->name, ce->name, ce->ce_namelen + 1);
- fill_stat_cache_info(updated, &st);
+ fill_stat_cache_info(istate, updated, &st);
/*
* If ignore_valid is not set, we should leave CE_VALID bit
* alone. Otherwise, paths marked with --no-assume-unchanged
if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
die(_("%s: index file smaller than expected"), path);
- mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ mmap = xmmap_gently(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mmap == MAP_FAILED)
die_errno(_("%s: unable to map index file"), path);
close(fd);
#include "cache.h"
+#include "dir.h"
#include "repository.h"
#include "refs.h"
#include "object.h"
{
struct packed_git *p;
int stale;
- int i = 0;
+ int i;
+ size_t alloc = 0;
for (p = get_all_packs(the_repository); p; p = p->next) {
/* we ignore things on alternate path since they are
* not available to the pullers in general.
*/
- if (!p->pack_local)
- continue;
- i++;
- }
- num_pack = i;
- info = xcalloc(num_pack, sizeof(struct pack_info *));
- for (i = 0, p = get_all_packs(the_repository); p; p = p->next) {
- if (!p->pack_local)
+ if (!p->pack_local || !file_exists(p->pack_name))
continue;
- assert(i < num_pack);
+
+ i = num_pack++;
+ ALLOC_GROW(info, num_pack, alloc);
info[i] = xcalloc(1, sizeof(struct pack_info));
info[i]->p = p;
info[i]->old_num = -1;
- i++;
}
if (infofile && !force)
/* Check if it is a missing object */
if (fetch_if_missing && repository_format_partial_clone &&
!already_retried && r == the_repository &&
- !(flags & OBJECT_INFO_FOR_PREFETCH)) {
+ !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
/*
* TODO Investigate having fetch_object() return
* TODO error/success and stopping the music here.
test_expect_success 'init with --template' '
mkdir template-source &&
echo content >template-source/file &&
- git init --template=../template-source template-custom &&
+ git init --template=template-source template-custom &&
test_cmp template-source/file template-custom/.git/file
'
(
cd new &&
umask 002 &&
- git init --shared=0660 --template=../templates &&
+ git init --shared=0660 --template=templates &&
>frotz &&
git add frotz &&
git commit -a -m initial &&
umask 0022 &&
git init --bare --shared=0666 child.git &&
test_path_is_missing child.git/foo &&
- git init --bare --template=../templates child.git &&
+ git init --bare --template=templates child.git &&
echo "-rw-rw-rw-" >expect &&
test_modebits child.git/foo >actual &&
test_cmp expect actual
umask 0022 &&
git config core.sharedrepository 0666 &&
cp .git/config templates/config &&
- git init --bare --template=../templates child.git &&
+ git init --bare --template=templates child.git &&
echo "-rw-rw-rw-" >expect &&
test_modebits child.git/HEAD >actual &&
test_cmp expect actual
git worktree add --force --force --detach gnoo
'
+test_expect_success '"add" should not fail because of another bad worktree' '
+ git init add-fail &&
+ (
+ cd add-fail &&
+ test_commit first &&
+ mkdir sub &&
+ git worktree add sub/to-be-deleted &&
+ rm -rf sub &&
+ git worktree add second
+ )
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='am --interactive tests'
+. ./test-lib.sh
+
+test_expect_success 'set up patches to apply' '
+ test_commit unrelated &&
+ test_commit no-conflict &&
+ test_commit conflict-patch file patch &&
+ git format-patch --stdout -2 >mbox &&
+
+ git reset --hard unrelated &&
+ test_commit conflict-master file master base
+'
+
+# Sanity check our setup.
+test_expect_success 'applying all patches generates conflict' '
+ test_must_fail git am mbox &&
+ echo resolved >file &&
+ git add -u &&
+ git am --resolved
+'
+
+test_expect_success 'interactive am can apply a single patch' '
+ git reset --hard base &&
+ # apply the first, but not the second
+ test_write_lines y n | git am -i mbox &&
+
+ echo no-conflict >expect &&
+ git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interactive am can resolve conflict' '
+ git reset --hard base &&
+ # apply both; the second one will conflict
+ test_write_lines y y | test_must_fail git am -i mbox &&
+ echo resolved >file &&
+ git add -u &&
+ # interactive "--resolved" will ask us if we want to apply the result
+ echo y | git am -i --resolved &&
+
+ echo conflict-patch >expect &&
+ git log -1 --format=%s >actual &&
+ test_cmp expect actual &&
+
+ echo resolved >expect &&
+ git cat-file blob HEAD:file >actual &&
+ test_cmp expect actual
+'
+
+test_done
'
+test_expect_success 'request-pull quotes regex metacharacters properly' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git tag -mrelease v2.0 &&
+ git push origin refs/tags/v2.0:refs/tags/v2-0 &&
+ test_must_fail git request-pull initial "$downstream_url" tags/v2.0 \
+ 2>../err
+ ) &&
+ grep "No match for commit .*" err &&
+ grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request with mismatched object' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push origin HEAD:refs/tags/full &&
+ test_must_fail git request-pull initial "$downstream_url" tags/full \
+ 2>../err
+ ) &&
+ grep "points to a different object" err &&
+ grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request with stale object' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push origin refs/tags/full &&
+ git tag -f -m"Thirty-one days" full &&
+ test_must_fail git request-pull initial "$downstream_url" tags/full \
+ 2>../err
+ ) &&
+ grep "points to a different object" err &&
+ grep "Are you sure you pushed" err
+
+'
+
test_done
test_must_fail git -C original push pushee-namespaced master
'
+test_expect_success 'set up ambiguous HEAD' '
+ git init ambiguous &&
+ (
+ cd ambiguous &&
+ git commit --allow-empty -m foo &&
+ git update-ref refs/namespaces/ns/refs/heads/one HEAD &&
+ git update-ref refs/namespaces/ns/refs/heads/two HEAD &&
+ git symbolic-ref refs/namespaces/ns/HEAD \
+ refs/namespaces/ns/refs/heads/two
+ )
+'
+
+test_expect_success 'clone chooses correct HEAD (v0)' '
+ GIT_NAMESPACE=ns git -c protocol.version=0 \
+ clone ambiguous ambiguous-v0 &&
+ echo refs/heads/two >expect &&
+ git -C ambiguous-v0 symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone chooses correct HEAD (v2)' '
+ GIT_NAMESPACE=ns git -c protocol.version=2 \
+ clone ambiguous ambiguous-v2 &&
+ echo refs/heads/two >expect &&
+ git -C ambiguous-v2 symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
test_expect_success 'invalid Content-Type rejected' '
test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
- grep "not valid:" actual
+ test_i18ngrep "not valid:" actual
'
test_expect_success 'create namespaced refs' '
)
'
-test_expect_success 'large fetch-pack requests can be split across POSTs' '
+test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
- grep "^=> Send header: POST" err >posts &&
- test_line_count = 2 posts
+ grep "^=> Send header: Transfer-Encoding: chunked" err
'
test_expect_success 'test allowreachablesha1inwant' '
test_expect_success 'server-side error detected' '
test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
- grep "server-side error" actual
+ test_i18ngrep "server-side error" actual
'
test_done
test_i18ngrep "the following paths have collided" icasefs/warning
'
-partial_clone () {
+partial_clone_server () {
SERVER="$1" &&
- URL="$2" &&
rm -rf "$SERVER" client &&
test_create_repo "$SERVER" &&
test_commit -C "$SERVER" two &&
HASH2=$(git hash-object "$SERVER/two.t") &&
test_config -C "$SERVER" uploadpack.allowfilter 1 &&
- test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+ test_config -C "$SERVER" uploadpack.allowanysha1inwant 1
+}
+partial_clone () {
+ SERVER="$1" &&
+ URL="$2" &&
+
+ partial_clone_server "${SERVER}" &&
git clone --filter=blob:limit=0 "$URL" client &&
git -C client fsck &&
partial_clone server "file://$(pwd)/server"
'
+test_expect_success 'partial clone with -o' '
+ partial_clone_server server &&
+ git clone -o blah --filter=blob:limit=0 "file://$(pwd)/server" client
+'
+
test_expect_success 'partial clone: warn if server does not support object filtering' '
rm -rf server client &&
test_create_repo server &&
git tag -d third
'
+test_expect_success '"verify" needs a worktree' '
+ git bundle create tip.bundle -1 master &&
+ test_must_fail nongit git bundle verify ../tip.bundle 2>err &&
+ test_i18ngrep "need a repository" err
+'
+
test_expect_success 'annotated tags can be excluded by rev-list options' '
git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 &&
git ls-remote bundle > output &&
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
-# Converts bytes into a form suitable for inclusion in a sed command. For
-# example, "printf 'ab\r\n' | hex_unpack" results in '\x61\x62\x0d\x0a'.
-sed_escape () {
- perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)' |
- sed 's/\(..\)/\\x\1/g'
+# Converts bytes into their hexadecimal representation. For example,
+# "printf 'ab\r\n' | hex_unpack" results in '61620d0a'.
+hex_unpack () {
+ perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)'
+}
+
+# Inserts $1 at the start of the string and every 2 characters thereafter.
+intersperse () {
+ sed 's/\(..\)/'$1'\1/g'
+}
+
+# Create a one-time-sed command to replace the existing packfile with $1.
+replace_packfile () {
+ # The protocol requires that the packfile be sent in sideband 1, hence
+ # the extra \x01 byte at the beginning.
+ printf "1,/packfile/!c %04x\\\\x01%s0000" \
+ "$(($(wc -c <$1) + 5))" \
+ "$(hex_unpack <$1 | intersperse '\\x')" \
+ >"$HTTPD_ROOT_PATH/one-time-sed"
}
test_expect_success 'upon cloning, check that all refs point to objects' '
# Replace the existing packfile with the crafted one. The protocol
# requires that the packfile be sent in sideband 1, hence the extra
# \x01 byte at the beginning.
- printf "1,/packfile/!c %04x\\\\x01%s0000" \
- "$(($(wc -c <incomplete.pack) + 5))" \
- "$(sed_escape <incomplete.pack)" \
- >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ replace_packfile incomplete.pack &&
# Use protocol v2 because the sed command looks for the "packfile"
# section header.
# Replace the existing packfile with the crafted one. The protocol
# requires that the packfile be sent in sideband 1, hence the extra
# \x01 byte at the beginning.
- printf "1,/packfile/!c %04x\\\\x01%s0000" \
- "$(($(wc -c <incomplete.pack) + 5))" \
- "$(sed_escape <incomplete.pack)" \
- >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ replace_packfile incomplete.pack &&
# Use protocol v2 because the sed command looks for the "packfile"
# section header.
! test -e "$HTTPD_ROOT_PATH/one-time-sed"
'
+test_expect_success 'tolerate server sending REF_DELTA against missing promisor objects' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ rm -rf "$SERVER" repo &&
+ test_create_repo "$SERVER" &&
+ test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+ test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+ # Create a commit with 2 blobs to be used as delta bases.
+ for i in $(test_seq 10)
+ do
+ echo "this is a line" >>"$SERVER/foo.txt" &&
+ echo "this is another line" >>"$SERVER/have.txt"
+ done &&
+ git -C "$SERVER" add foo.txt have.txt &&
+ git -C "$SERVER" commit -m bar &&
+ git -C "$SERVER" rev-parse HEAD:foo.txt >deltabase_missing &&
+ git -C "$SERVER" rev-parse HEAD:have.txt >deltabase_have &&
+
+ # Clone. The client has deltabase_have but not deltabase_missing.
+ git -c protocol.version=2 clone --no-checkout \
+ --filter=blob:none $HTTPD_URL/one_time_sed/server repo &&
+ git -C repo hash-object -w -- "$SERVER/have.txt" &&
+
+ # Sanity check to ensure that the client does not have
+ # deltabase_missing.
+ git -C repo rev-list --objects --ignore-missing \
+ -- $(cat deltabase_missing) >objlist &&
+ test_line_count = 0 objlist &&
+
+ # Another commit. This commit will be fetched by the client.
+ echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/foo.txt" &&
+ echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/have.txt" &&
+ git -C "$SERVER" add foo.txt have.txt &&
+ git -C "$SERVER" commit -m baz &&
+
+ # Pack a thin pack containing, among other things, HEAD:foo.txt
+ # delta-ed against HEAD^:foo.txt and HEAD:have.txt delta-ed against
+ # HEAD^:have.txt.
+ printf "%s\n--not\n%s\n" \
+ $(git -C "$SERVER" rev-parse HEAD) \
+ $(git -C "$SERVER" rev-parse HEAD^) |
+ git -C "$SERVER" pack-objects --thin --stdout >thin.pack &&
+
+ # Ensure that the pack contains one delta against HEAD^:foo.txt. Since
+ # the delta contains at least 26 novel characters, the size cannot be
+ # contained in 4 bits, so the object header will take up 2 bytes. The
+ # most significant nybble of the first byte is 0b1111 (0b1 to indicate
+ # that the header continues, and 0b111 to indicate REF_DELTA), followed
+ # by any 3 nybbles, then the OID of the delta base.
+ printf "f.,..%s" $(intersperse "," <deltabase_missing) >want &&
+ hex_unpack <thin.pack | intersperse "," >have &&
+ grep $(cat want) have &&
+
+ # Ensure that the pack contains one delta against HEAD^:have.txt,
+ # similar to the above.
+ printf "f.,..%s" $(intersperse "," <deltabase_have) >want &&
+ hex_unpack <thin.pack | intersperse "," >have &&
+ grep $(cat want) have &&
+
+ replace_packfile thin.pack &&
+
+ # Use protocol v2 because the sed command looks for the "packfile"
+ # section header.
+ test_config -C "$SERVER" protocol.version 2 &&
+
+ # Fetch the thin pack and ensure that index-pack is able to handle the
+ # REF_DELTA object with a missing promisor delta base.
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C repo -c protocol.version=2 fetch &&
+
+ # Ensure that the missing delta base was directly fetched, but not the
+ # one that the client has.
+ grep "want $(cat deltabase_missing)" trace &&
+ ! grep "want $(cat deltabase_have)" trace &&
+
+ # Ensure that the one-time-sed script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
+'
+
test_done
git gc --keep-largest-pack &&
( cd .git/objects/pack && ls *.pack ) >pack-list &&
test_line_count = 2 pack-list &&
+ awk "/^P /{print \$2}" <.git/objects/info/packs >pack-info &&
+ test_line_count = 2 pack-info &&
test_path_is_file $BASE_PACK &&
git fsck
)
test_must_fail git merge --no-ff --squash c1
'
+test_expect_success 'combining --squash and --commit is refused' '
+ git reset --hard c0 &&
+ test_must_fail git merge --squash --commit c1 &&
+ test_must_fail git merge --commit --squash c1
+'
+
test_expect_success 'option --ff-only overwrites --no-ff' '
git merge --no-ff --ff-only c1 &&
test_must_fail git merge --no-ff --ff-only c2
: >file_to_move &&
p4 add file_to_delete &&
p4 add file_to_move &&
- p4 submit -d "add files to delete"
+ p4 submit -d "add files to delete" &&
+ echo file_to_integrate >file_to_integrate &&
+ p4 add file_to_integrate &&
+ p4 submit -d "add file to integrate"
)
'
p4 delete file_to_delete &&
p4 edit file_to_move &&
p4 move file_to_move moved_file &&
+ p4 integrate file_to_integrate integrated_file &&
p4 opened &&
p4 shelve -i <<EOF
Change: new
//depot/file_to_delete
//depot/file_to_move
//depot/moved_file
+ //depot/integrated_file
EOF
) &&
test_path_is_file file2 &&
test_cmp file1 "$cli"/file1 &&
test_cmp file2 "$cli"/file2 &&
+ test_cmp file_to_integrate "$cli"/integrated_file &&
test_path_is_missing file_to_delete &&
test_path_is_missing file_to_move &&
test_path_is_file moved_file
struct helper_data *data = transport->data;
int cat_blob_fd, code;
child_process_init(fastimport);
- fastimport->in = helper->out;
+ fastimport->in = xdup(helper->out);
argv_array_push(&fastimport->args, "fast-import");
argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
{ 0x31C0, 0x31E3 },
{ 0x31F0, 0x321E },
{ 0x3220, 0x3247 },
-{ 0x3250, 0x32FE },
-{ 0x3300, 0x4DBF },
+{ 0x3250, 0x4DBF },
{ 0x4E00, 0xA48C },
{ 0xA490, 0xA4C6 },
{ 0xA960, 0xA97C },
symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, refname);
- item->util = xstrdup(symref_target);
+ item = string_list_append(cb_data, strip_namespace(refname));
+ item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
break;
}
- if (c == '%') {
+ if (c == '%' && (len < 0 || len >= 3)) {
int val = hex2chr(q + 1);
- if (0 <= val) {
+ if (0 < val) {
strbuf_addch(out, val);
q += 3;
len -= 3;
free(to_free);
return NULL;
}
- for (; *list; list++)
- if (!fspathcmp(path, real_path((*list)->path)))
+ for (; *list; list++) {
+ const char *wt_path = real_path_if_valid((*list)->path);
+
+ if (wt_path && !fspathcmp(path, wt_path))
break;
+ }
free(path);
free(to_free);
return *list;