Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>
René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx>
+René Scharfe <l.s.r@web.de> Rene Scharfe
Richard Hansen <rhansen@rhansen.org> <hansenr@google.com>
Richard Hansen <rhansen@rhansen.org> <rhansen@bbn.com>
Robert Fitzsimons <robfitz@273k.net>
services:
- docker
before_install:
- - docker pull daald/ubuntu32:xenial
before_script:
- script:
- - >
- docker run
- --interactive
- --env DEVELOPER
- --env DEFAULT_TEST_TARGET
- --env GIT_PROVE_OPTS
- --env GIT_TEST_OPTS
- --env GIT_TEST_CLONE_2GB
- --volume "${PWD}:/usr/src/git"
- daald/ubuntu32:xenial
- /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
- # Use the following command to debug the docker build locally:
- # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
- # root@container:/# /usr/src/git/ci/run-linux32-build.sh
+ script: ci/run-linux32-docker.sh
- env: Static Analysis
os: linux
compiler:
packages:
- coccinelle
before_install:
- script:
- # "before_script" that builds Git is inherited from base job
- - make coccicheck
+ # "before_script" that builds Git is inherited from base job
+ script: ci/run-static-analysis.sh
after_failure:
- env: Documentation
os: linux
- asciidoc
- xmlto
before_install:
- before_script: gem install asciidoctor
+ before_script:
script: ci/test-documentation.sh
after_failure:
-before_install:
- - >
- case "${TRAVIS_OS_NAME:-linux}" in
- linux)
- export GIT_TEST_HTTPD=YesPlease
-
- mkdir --parents custom/p4
- pushd custom/p4
- wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4d
- wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4
- chmod u+x p4d
- chmod u+x p4
- export PATH="$(pwd):$PATH"
- popd
- mkdir --parents custom/git-lfs
- pushd custom/git-lfs
- wget --quiet https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz
- tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
- cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
- export PATH="$(pwd):$PATH"
- popd
- ;;
- osx)
- brew update --quiet
- # Uncomment this if you want to run perf tests:
- # brew install gnu-time
- brew install git-lfs gettext
- brew link --force gettext
- brew install caskroom/cask/perforce
- ;;
- esac;
- echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)";
- p4d -V | grep Rev.;
- echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)";
- p4 -V | grep Rev.;
- echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)";
- git-lfs version;
-
-before_script: make --jobs=2
-
-script:
- - >
- mkdir -p $HOME/travis-cache;
- ln -s $HOME/travis-cache/.prove t/.prove;
- make --quiet test;
-
-after_failure:
- - >
- : '<-- Click here to see detailed test output! ';
- for TEST_EXIT in t/test-results/*.exit;
- do
- if [ "$(cat "$TEST_EXIT")" != "0" ];
- then
- TEST_OUT="${TEST_EXIT%exit}out";
- echo "------------------------------------------------------------------------";
- echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)";
- echo "------------------------------------------------------------------------";
- cat "${TEST_OUT}";
- fi;
- done;
+before_install: ci/install-dependencies.sh
+before_script: ci/run-build.sh
+script: ci/run-tests.sh
+after_failure: ci/print-test-failures.sh
notifications:
email: false
--- /dev/null
+# Suppressions for ThreadSanitizer (tsan).
+#
+# This file is used by setting the environment variable TSAN_OPTIONS to, e.g.,
+# "suppressions=$(pwd)/.tsan-suppressions". Observe that relative paths such as
+# ".tsan-suppressions" might not work.
+
+# A static variable is written to racily, but we always write the same value, so
+# in practice it (hopefully!) doesn't matter.
+race:^want_color$
+race:^transfer_debug$
--- /dev/null
+Git v2.14.3 Release Notes
+=========================
+
+Fixes since v2.14.2
+-------------------
+
+ * A helper function to read a single whole line into strbuf
+ mistakenly triggered OOM error at EOF under certain conditions,
+ which has been fixed.
+
+ * In addition to "cc: <a@dd.re.ss> # cruft", "cc: a@dd.re.ss # cruft"
+ was taught to "git send-email" as a valid way to tell it that it
+ needs to also send a carbon copy to <a@dd.re.ss> in the trailer
+ section.
+
+ * Fix regression to "gitk --bisect" by a recent update.
+
+ * Unlike "git commit-tree < file", "git commit-tree -F file" did not
+ pass the contents of the file verbatim and instead completed an
+ incomplete line at the end, if exists. The latter has been updated
+ to match the behaviour of the former.
+
+ * "git archive", especially when used with pathspec, stored an empty
+ directory in its output, even though Git itself never does so.
+ This has been fixed.
+
+ * API error-proofing which happens to also squelch warnings from GCC.
+
+ * "git gc" tries to avoid running two instances at the same time by
+ reading and writing pid/host from and to a lock file; it used to
+ use an incorrect fscanf() format when reading, which has been
+ corrected.
+
+ * The test linter has been taught that we do not like "echo -e".
+
+ * Code cmp.std.c nitpick.
+
+ * "git describe --match" learned to take multiple patterns in v2.13
+ series, but the feature ignored the patterns after the first one
+ and did not work at all. This has been fixed.
+
+ * "git cat-file --textconv" started segfaulting recently, which
+ has been corrected.
+
+ * The built-in pattern to detect the "function header" for HTML did
+ not match <H1>..<H6> elements without any attributes, which has
+ been fixed.
+
+ * "git mailinfo" was loose in decoding quoted printable and produced
+ garbage when the two letters after the equal sign are not
+ hexadecimal. This has been fixed.
+
+ * The documentation for '-X<option>' for merges was misleadingly
+ written to suggest that "-s theirs" exists, which is not the case.
+
+ * Spell the name of our system as "Git" in the output from
+ request-pull script.
+
+ * Fixes for a handful memory access issues identified by valgrind.
+
+ * Backports a moral equivalent of 2015 fix to the poll emulation from
+ the upstream gnulib to fix occasional breakages on HPE NonStop.
+
+ * In the "--format=..." option of the "git for-each-ref" command (and
+ its friends, i.e. the listing mode of "git branch/tag"), "%(atom:)"
+ (e.g. "%(refname:)", "%(body:)" used to error out. Instead, treat
+ them as if the colon and an empty string that follows it were not
+ there.
+
+ * Users with "color.ui = always" in their configuration were broken
+ by a recent change that made plumbing commands to pay attention to
+ them as the patch created internally by "git add -p" were colored
+ (heh) and made unusable. This has been fixed.
+
+ * "git branch -M a b" while on a branch that is completely unrelated
+ to either branch a or branch b misbehaved when multiple worktree
+ was in use. This has been fixed.
+
+ * "git fast-export" with -M/-C option issued "copy" instruction on a
+ path that is simultaneously modified, which was incorrect.
+
+ * The checkpoint command "git fast-import" did not flush updates to
+ refs and marks unless at least one object was created since the
+ last checkpoint, which has been corrected, as these things can
+ happen without any new object getting created.
+
+ * The scripts to drive TravisCI has been reorganized and then an
+ optimization to avoid spending cycles on a branch whose tip is
+ tagged has been implemented.
+
+ * "git fetch <there> <src>:<dst>" allows an object name on the <src>
+ side when the other side accepts such a request since Git v2.5, but
+ the documentation was left stale.
+
+ * A regression in 2.11 that made the code to read the list of
+ alternate object stores overrun the end of the string has been
+ fixed.
+
+Also contains various documentation updates and code clean-ups.
the working tree. Note that older versions of Git used
to ignore removed files; use `--no-all` option if you want
to add modified or new files but ignore removed ones.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
-n::
--dry-run::
all changes made to the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@\{yesterday}".
Note that in non-bare repositories, reflogs are usually
- enabled by default by the `core.logallrefupdates` config option.
+ enabled by default by the `core.logAllRefUpdates` config option.
The negated form `--no-create-reflog` only overrides an earlier
`--create-reflog`, but currently does not negate the setting of
- `core.logallrefupdates`.
+ `core.logAllRefUpdates`.
-f::
--force::
The 40-hex object name of the object.
`objecttype`::
- The type of of the object (the same as `cat-file -t` reports).
+ The type of the object (the same as `cat-file -t` reports).
`objectsize`::
The size, in bytes, of the object (the same as `cat-file -s`
'git checkout' [-q] [-f] [-m] [--detach] <commit>
'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
-'git checkout' [-p|--patch] [<tree-ish>] [--] [<paths>...]
+'git checkout' [<tree-ish>] [--] <pathspec>...
+'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...]
DESCRIPTION
-----------
------------
+
You could omit <branch>, in which case the command degenerates to
-"check out the current branch", which is a glorified no-op with a
+"check out the current branch", which is a glorified no-op with
rather expensive side-effects to show only the tracking information,
if exists, for the current branch.
+
Omitting <branch> detaches HEAD at the tip of the current branch.
-'git checkout' [-p|--patch] [<tree-ish>] [--] <pathspec>...::
+'git checkout' [<tree-ish>] [--] <pathspec>...::
- When <paths> or `--patch` are given, 'git checkout' does *not*
- switch branches. It updates the named paths in the working tree
- from the index file or from a named <tree-ish> (most often a
- commit). In this case, the `-b` and `--track` options are
- meaningless and giving either of them results in an error. The
- <tree-ish> argument can be used to specify a specific tree-ish
- (i.e. commit, tag or tree) to update the index for the given
- paths before updating the working tree.
-+
-'git checkout' with <paths> or `--patch` is used to restore modified or
-deleted paths to their original contents from the index or replace paths
-with the contents from a named <tree-ish> (most often a commit-ish).
+ Overwrite paths in the working tree by replacing with the
+ contents in the index or in the <tree-ish> (most often a
+ commit). When a <tree-ish> is given, the paths that
+ match the <pathspec> are updated both in the index and in
+ the working tree.
+
The index may contain unmerged entries because of a previous failed merge.
By default, if you try to check out such an entry from the index, the
using `--ours` or `--theirs`. With `-m`, changes made to the working tree
file can be discarded to re-create the original conflicted merge result.
+'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]::
+ This is similar to the "check out paths to the working tree
+ from either the index or from a tree-ish" mode described
+ above, but lets you use the interactive interface to show
+ the "diff" output and choose which hunks to use in the
+ result. See below for the description of `--patch` option.
+
+
OPTIONS
-------
-q::
[verse]
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
- [--points-at <object>] [(--merged | --no-merged) [<object>]]
- [--contains [<object>]] [--no-contains [<object>]]
+ [--points-at=<object>]
+ (--merged[=<object>] | --no-merged[=<object>])
+ [--contains[=<object>]] [--no-contains[=<object>]]
DESCRIPTION
-----------
OPTIONS
-------
-<count>::
+<pattern>...::
+ If one or more patterns are given, only refs are shown that
+ match against at least one pattern, either using fnmatch(3) or
+ literally, in the latter case matching completely or from the
+ beginning up to a slash.
+
+--count=<count>::
By default the command shows all refs that match
`<pattern>`. This option makes it stop after showing
that many refs.
-<key>::
+--sort=<key>::
A field name to sort on. Prefix `-` to sort in
descending order of the value. When unspecified,
`refname` is used. You may use the --sort=<key> option
multiple times, in which case the last key becomes the primary
key.
-<format>::
+--format=<format>::
A string that interpolates `%(fieldname)` from a ref being shown
and the object it points at. If `fieldname`
is prefixed with an asterisk (`*`) and the ref points
`xx`; for example `%00` interpolates to `\0` (NUL),
`%09` to `\t` (TAB) and `%0a` to `\n` (LF).
-<pattern>...::
- If one or more patterns are given, only refs are shown that
- match against at least one pattern, either using fnmatch(3) or
- literally, in the latter case matching completely or from the
- beginning up to a slash.
+--color[=<when>]:
+ Respect any colors specified in the `--format` option. The
+ `<when>` field must be one of `always`, `never`, or `auto` (if
+ `<when>` is absent, behave as if `always` was given).
--shell::
--perl::
the specified host language. This is meant to produce
a scriptlet that can directly be `eval`ed.
---points-at <object>::
+--points-at=<object>::
Only list refs which points at the given object.
---merged [<object>]::
+--merged[=<object>]::
Only list refs whose tips are reachable from the
specified commit (HEAD if not specified),
incompatible with `--no-merged`.
---no-merged [<object>]::
+--no-merged[=<object>]::
Only list refs whose tips are not reachable from the
specified commit (HEAD if not specified),
incompatible with `--merged`.
---contains [<object>]::
+--contains[=<object>]::
Only list refs which contain the specified commit (HEAD if not
specified).
---no-contains [<object>]::
+--no-contains[=<object>]::
Only list refs which don't contain the specified commit (HEAD
if not specified).
<pathspec>...::
If given, limit the search to paths matching at least one pattern.
Both leading paths match and glob(7) patterns are supported.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
Examples
--------
Looks for a line that has `NODE` or `Unexpected` in
files that have lines that match both.
+`git grep solution -- :^Documentation`::
+ Looks for `solution`, excluding files in `Documentation`.
+
GIT
---
Part of the linkgit:git[1] suite
object that does not have notes attached to it.
--stdin::
- Also read the object names to remove notes from from the standard
+ Also read the object names to remove notes from the standard
input (there is no reason you cannot combine this with object
names from the command line).
"clean" means that index and work tree coincide, and "exists"/"nothing"
refer to the presence of a path in the specified commit:
+....
I H M Result
-------------------------------------------------------
0 nothing nothing nothing (does not happen)
19 no no yes exists exists keep index
20 yes yes no exists exists use M
21 no yes no exists exists fail
+....
In all "keep index" cases, the index entry stays as in the
original index file. If the entry is not up to date,
without options are equivalent to 'always' and 'never'
respectively.
+<pathspec>...::
+ See the 'pathspec' entry in linkgit:gitglossary[7].
OUTPUT
------
variable if it exists, or lexicographic order otherwise. See
linkgit:git-config[1].
+--color[=<when>]:
+ Respect any colors specified in the `--format` option. The
+ `<when>` field must be one of `always`, `never`, or `auto` (if
+ `<when>` is absent, behave as if `always` was given).
+
-i::
--ignore-case::
Sorting and filtering tags are case insensitive.
`core.logAllRefUpdates` in linkgit:git-config[1].
The negated form `--no-create-reflog` only overrides an earlier
`--create-reflog`, but currently does not negate the setting of
- `core.logallrefupdates`.
+ `core.logAllRefUpdates`.
<tagname>::
The name of the tag to create, delete, or describe.
+
Version 4 performs a simple pathname compression that reduces index
size by 30%-50% on large repositories, which results in faster load
-time. Version 4 is relatively young (first released in in 1.8.0 in
+time. Version 4 is relatively young (first released in 1.8.0 in
October 2012). Other Git implementations such as JGit and libgit2
may not support it yet.
Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets
`foo.bar` to the boolean true value (just like `[foo]bar` would in a
config file). Including the equals but with an empty value (like `git -c
-foo.bar= ...`) sets `foo.bar` to the empty string which ` git config
+foo.bar= ...`) sets `foo.bar` to the empty string which `git config
--bool` will convert to `false`.
--exec-path[=<path>]::
This attribute sets a specific line-ending style to be used in the
working directory. It enables end-of-line conversion without any
-content checks, effectively setting the `text` attribute.
+content checks, effectively setting the `text` attribute. Note that
+setting this attribute on paths which are in the index with CRLF line
+endings may make the paths to be considered dirty. Adding the path to
+the index again will normalize the line endings in the index.
Set to string value "crlf"::
exclude;;
After a path matches any non-exclude pathspec, it will be run
- through all exclude pathspec (magic signature: `!` or its
+ through all exclude pathspecs (magic signature: `!` or its
synonym `^`). If it matches, the path is ignored. When there
is no non-exclude pathspec, the exclusion is applied to the
result set as if invoked without any pathspec.
the other tree did, declaring 'our' history contains all that happened in it.
theirs;;
- This is the opposite of 'ours'.
+ This is the opposite of 'ours'; note that, unlike 'ours', there is
+ no 'theirs' merge stragegy to confuse this merge option with.
patience;;
With this option, 'merge-recursive' spends a little extra time
endif::git-pull[]
+
The format of a <refspec> parameter is an optional plus
-`+`, followed by the source ref <src>, followed
+`+`, followed by the source <src>, followed
by a colon `:`, followed by the destination ref <dst>.
-The colon can be omitted when <dst> is empty.
+The colon can be omitted when <dst> is empty. <src> is
+typically a ref, but it can also be a fully spelled hex object
+name.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
it requests fetching everything up to the given tag.
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.14.2
+DEF_VER=v2.14.3
LF='
'
-Documentation/RelNotes/2.14.2.txt
\ No newline at end of file
+Documentation/RelNotes/2.14.3.txt
\ No newline at end of file
return check && ATTR_TRUE(check->items[1].value);
}
-static int should_queue_directories(const struct archiver_args *args)
-{
- return args->pathspec.has_wildcard;
-}
-
static int write_archive_entry(const unsigned char *sha1, const char *base,
int baselen, const char *filename, unsigned mode, int stage,
void *context)
strbuf_addch(&path, '/');
path_without_prefix = path.buf + args->baselen;
- if (!S_ISDIR(mode) || !should_queue_directories(args)) {
+ if (!S_ISDIR(mode)) {
const struct attr_check *check;
check = get_archive_attrs(path_without_prefix);
if (check_attr_export_ignore(check))
return write_entry(args, sha1, path.buf, path.len, mode);
}
-static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
- const char *filename, unsigned mode, int stage,
- void *context)
-{
- return write_archive_entry(sha1, base->buf, base->len,
- filename, mode, stage, context);
-}
-
static void queue_directory(const unsigned char *sha1,
struct strbuf *base, const char *filename,
unsigned mode, int stage, struct archiver_context *c)
}
err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
- should_queue_directories(args) ?
- queue_or_write_archive_entry :
- write_archive_entry_buf,
+ queue_or_write_archive_entry,
&context);
if (err == READ_TREE_RECURSIVE)
err = 0;
if (worktrees[i]->is_detached)
continue;
- if (worktrees[i]->head_ref &&
- strcmp(oldref, worktrees[i]->head_ref))
+ if (!worktrees[i]->head_ref)
+ continue;
+ if (strcmp(oldref, worktrees[i]->head_ref))
continue;
refs = get_worktree_ref_store(worktrees[i]);
return config_error_nonbool(var);
return color_parse(value, branch_colors[slot]);
}
- return git_default_config(var, value, cb);
+ return git_color_default_config(var, value, cb);
}
static const char *branch_get_color(enum color_branch ix)
if (!head_rev)
die(_("Couldn't look up commit object for HEAD"));
}
- for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+ for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
int flags = 0;
}
free(name);
+ strbuf_release(&bname);
- return(ret);
+ return ret;
}
static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
return !has_object_file(&oid);
case 'w':
- if (!path[0])
+ if (!path)
die("git cat-file --filters %s: <object> must be "
"<sha1:path>", obj_name);
break;
case 'c':
- if (!path[0])
+ if (!path)
die("git cat-file --textconv %s: <object> must be <sha1:path>",
obj_name);
return 0;
}
- return git_default_config(var, value, cb);
+ /* inspect the color.ui config variable and others */
+ return git_color_default_config(var, value, cb);
}
static const char *clean_get_color(enum color_clean ix)
if (fd && close(fd))
die_errno("git commit-tree: failed to close '%s'",
argv[i]);
- strbuf_complete_line(&buffer);
continue;
}
* pattern.
*/
if (patterns.nr) {
+ int found = 0;
struct string_list_item *item;
if (!is_tag)
return 0;
for_each_string_list_item(item, &patterns) {
- if (!wildmatch(item->string, path + 10, 0))
+ if (!wildmatch(item->string, path + 10, 0)) {
+ found = 1;
break;
+ }
+ }
- /* If we get here, no pattern matched. */
+ if (!found)
return 0;
- }
}
/* Is it annotated? */
struct diff_options *options, void *data)
{
int i;
+ struct string_list *changed = data;
/*
* Handle files below a directory first, in case they are all deleted
case DIFF_STATUS_DELETED:
printf("D ");
print_path(spec->path);
+ string_list_insert(changed, spec->path);
putchar('\n');
break;
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
- printf("%c ", q->queue[i]->status);
- print_path(ospec->path);
- putchar(' ');
- print_path(spec->path);
- putchar('\n');
-
- if (!oidcmp(&ospec->oid, &spec->oid) &&
- ospec->mode == spec->mode)
- break;
+ /*
+ * If a change in the file corresponding to ospec->path
+ * has been observed, we cannot trust its contents
+ * because the diff is calculated based on the prior
+ * contents, not the current contents. So, declare a
+ * copy or rename only if there was no change observed.
+ */
+ if (!string_list_has_string(changed, ospec->path)) {
+ printf("%c ", q->queue[i]->status);
+ print_path(ospec->path);
+ putchar(' ');
+ print_path(spec->path);
+ string_list_insert(changed, spec->path);
+ putchar('\n');
+
+ if (!oidcmp(&ospec->oid, &spec->oid) &&
+ ospec->mode == spec->mode)
+ break;
+ }
/* fallthrough */
case DIFF_STATUS_TYPE_CHANGED:
get_object_mark(object));
}
print_path(spec->path);
+ string_list_insert(changed, spec->path);
putchar('\n');
break;
*end = out->buf + out->len;
}
-static void handle_commit(struct commit *commit, struct rev_info *rev)
+static void handle_commit(struct commit *commit, struct rev_info *rev,
+ struct string_list *paths_of_changed_objects)
{
int saved_output_format = rev->diffopt.output_format;
const char *commit_buffer;
if (full_tree)
printf("deleteall\n");
log_tree_diff_flush(rev);
+ string_list_clear(paths_of_changed_objects, 0);
rev->diffopt.output_format = saved_output_format;
printf("\n");
return strbuf_detach(&out, len);
}
-static void handle_tail(struct object_array *commits, struct rev_info *revs)
+static void handle_tail(struct object_array *commits, struct rev_info *revs,
+ struct string_list *paths_of_changed_objects)
{
struct commit *commit;
while (commits->nr) {
commit = (struct commit *)commits->objects[commits->nr - 1].item;
if (has_unshown_parent(commit))
return;
- handle_commit(commit, revs);
+ handle_commit(commit, revs, paths_of_changed_objects);
commits->nr--;
}
}
char *export_filename = NULL, *import_filename = NULL;
uint32_t lastimportid;
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
+ struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
struct option options[] = {
OPT_INTEGER(0, "progress", &progress,
N_("show progress after <n> objects")),
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
+ revs.diffopt.format_callback_data = &paths_of_changed_objects;
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
while ((commit = get_revision(&revs))) {
if (has_unshown_parent(commit)) {
add_object_array(&commit->object, NULL, &commits);
}
else {
- handle_commit(commit, &revs);
- handle_tail(&commits, &revs);
+ handle_commit(commit, &revs, &paths_of_changed_objects);
+ handle_tail(&commits, &revs, &paths_of_changed_objects);
}
}
OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
+ OPT__COLOR(&format.use_color, N_("respect format colors")),
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
N_("field name to sort on"), &parse_opt_ref_sorting),
OPT_CALLBACK(0, "points-at", &filter.points_at,
int should_exit;
if (!scan_fmt)
- scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
+ scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX);
fp = fopen(pidfile_path, "r");
memset(locking_host, 0, sizeof(locking_host));
should_exit =
if (!skip_prefix(content, "52 comment=", &comment))
return 1;
- n = write_in_full(1, comment, 41);
- if (n < 41)
+ if (write_in_full(1, comment, 41) < 0)
die_errno("git get-tar-commit-id: write error");
return 0;
static int grep_cmd_config(const char *var, const char *value, void *cb)
{
int st = grep_config(var, value, cb);
- if (git_default_config(var, value, cb) < 0)
+ if (git_color_default_config(var, value, cb) < 0)
st = -1;
if (!strcmp(var, "grep.threads")) {
struct commit *commit = (struct commit *)o;
int from_tag = starts_with(path, "refs/tags/");
- if (taggerdate == ULONG_MAX)
+ if (taggerdate == TIME_MAX)
taggerdate = ((struct commit *)o)->date;
path = name_ref_abbrev(path, can_abbreviate_output);
name_rev(commit, xstrdup(path), taggerdate, 0, 0,
{
struct thread_params *me = arg;
+ progress_lock();
while (me->remaining) {
+ progress_unlock();
+
find_deltas(me->list, &me->remaining,
me->window, me->depth, me->processed);
pthread_cond_wait(&me->cond, &me->mutex);
me->data_ready = 0;
pthread_mutex_unlock(&me->mutex);
+
+ progress_lock();
}
+ progress_unlock();
/* leave ->working 1 so that this doesn't get more work assigned */
return NULL;
}
};
struct in_pack {
- int alloc;
- int nr;
+ unsigned int alloc;
+ unsigned int nr;
struct in_pack_object *array;
};
size_t n;
if (feed(feed_state, &buf, &n))
break;
- if (write_in_full(proc.in, buf, n) != n)
+ if (write_in_full(proc.in, buf, n) < 0)
break;
}
close(proc.in);
{
int i;
for (i = 0; i < nbuf; i++)
- if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
+ if (write_in_full(1, ptr[i].ptr, ptr[i].size) < 0)
return -1;
return 0;
}
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_ref_in("refs/bisect/bad", show_reference, NULL);
- for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+ for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
+ for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
return 0;
}
- return git_default_config(var, value, cb);
+ return git_color_default_config(var, value, cb);
}
static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
- return git_default_config(var, value, cb);
+ return git_color_default_config(var, value, cb);
}
static void write_tag_body(int fd, const struct object_id *oid)
},
OPT_STRING( 0 , "format", &format.format, N_("format"),
N_("format to use for the output")),
+ OPT__COLOR(&format.use_color, N_("respect format colors")),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END()
};
xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
fd = xmkstemp(path);
- if (write_in_full(fd, buf, size) != size)
+ if (write_in_full(fd, buf, size) < 0)
die_errno("unable to write temp-file");
close(fd);
return path;
*/
static inline int hex2chr(const char *s)
{
- int val = hexval(s[0]);
- return (val < 0) ? val : (val << 4) | hexval(s[1]);
+ unsigned int val = hexval(s[0]);
+ return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
}
/* Convert to/from hex/sha1 representation */
--- /dev/null
+#!/usr/bin/env bash
+#
+# Install dependencies required to build and test Git on Linux and macOS
+#
+
+. ${0%/*}/lib-travisci.sh
+
+P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
+LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
+
+case "${TRAVIS_OS_NAME:-linux}" in
+linux)
+ export GIT_TEST_HTTPD=YesPlease
+
+ mkdir --parents custom/p4
+ pushd custom/p4
+ wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
+ wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
+ chmod u+x p4d
+ chmod u+x p4
+ export PATH="$(pwd):$PATH"
+ popd
+ mkdir --parents custom/git-lfs
+ pushd custom/git-lfs
+ wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
+ export PATH="$(pwd):$PATH"
+ popd
+ ;;
+osx)
+ brew update --quiet
+ # Uncomment this if you want to run perf tests:
+ # brew install gnu-time
+ brew install git-lfs gettext
+ brew link --force gettext
+ brew install caskroom/cask/perforce
+ ;;
+esac
+
+echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)"
+p4d -V | grep Rev.
+echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)"
+p4 -V | grep Rev.
+echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"
+git-lfs version
--- /dev/null
+# Library of functions shared by all CI scripts
+
+skip_branch_tip_with_tag () {
+ # Sometimes, a branch is pushed at the same time the tag that points
+ # at the same commit as the tip of the branch is pushed, and building
+ # both at the same time is a waste.
+ #
+ # Travis gives a tagname e.g. v2.14.0 in $TRAVIS_BRANCH when
+ # the build is triggered by a push to a tag. Let's see if
+ # $TRAVIS_BRANCH is exactly at a tag, and if so, if it is
+ # different from $TRAVIS_BRANCH. That way, we can tell if
+ # we are building the tip of a branch that is tagged and
+ # we can skip the build because we won't be skipping a build
+ # of a tag.
+
+ if TAG=$(git describe --exact-match "$TRAVIS_BRANCH" 2>/dev/null) &&
+ test "$TAG" != "$TRAVIS_BRANCH"
+ then
+ echo "Tip of $TRAVIS_BRANCH is exactly at $TAG"
+ exit 0
+ fi
+}
+
+# Set 'exit on error' for all CI scripts to let the caller know that
+# something went wrong
+set -e
+
+skip_branch_tip_with_tag
--- /dev/null
+#!/bin/sh
+#
+# Print output of failing tests
+#
+
+. ${0%/*}/lib-travisci.sh
+
+for TEST_EXIT in t/test-results/*.exit
+do
+ if [ "$(cat "$TEST_EXIT")" != "0" ]
+ then
+ TEST_OUT="${TEST_EXIT%exit}out"
+ echo "------------------------------------------------------------------------"
+ echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)"
+ echo "------------------------------------------------------------------------"
+ cat "${TEST_OUT}"
+ fi
+done
--- /dev/null
+#!/bin/sh
+#
+# Build Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+make --jobs=2
--- /dev/null
+#!/bin/sh
+#
+# Download and run Docker image to build and test 32-bit Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+docker pull daald/ubuntu32:xenial
+
+# Use the following command to debug the docker build locally:
+# $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
+# root@container:/# /usr/src/git/ci/run-linux32-build.sh
+
+docker run \
+ --interactive \
+ --env DEVELOPER \
+ --env DEFAULT_TEST_TARGET \
+ --env GIT_PROVE_OPTS \
+ --env GIT_TEST_OPTS \
+ --env GIT_TEST_CLONE_2GB \
+ --volume "${PWD}:/usr/src/git" \
+ daald/ubuntu32:xenial \
+ /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
--- /dev/null
+#!/bin/sh
+#
+# Perform various static code analysis checks
+#
+
+. ${0%/*}/lib-travisci.sh
+
+make coccicheck
--- /dev/null
+#!/bin/sh
+#
+# Test Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+mkdir -p $HOME/travis-cache
+ln -s $HOME/travis-cache/.prove t/.prove
+make --quiet test
# supported) and a commit hash.
#
+. ${0%/*}/lib-travisci.sh
+
test $# -ne 2 && echo "Unexpected number of parameters" && exit 1
test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit
# Perform sanity checks on documentation and build it.
#
-set -e
+. ${0%/*}/lib-travisci.sh
+
+gem install asciidoctor
make check-builtins
make check-docs
int want_color(int var)
{
+ /*
+ * NEEDSWORK: This function is sometimes used from multiple threads, and
+ * we end up using want_auto racily. That "should not matter" since
+ * we always write the same value, but it's still wrong. This function
+ * is listed in .tsan-suppressions for the time being.
+ */
+
static int want_auto = -1;
if (var < 0)
return 0;
}
+int git_color_default_config(const char *var, const char *value, void *cb)
+{
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, cb);
+}
+
void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
{
if (*color)
\
static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s) \
{ \
- int i; \
+ unsigned int i; \
for (i = 0; i < s->slab_count; i++) \
free(s->slab[i]); \
s->slab_count = 0; \
const struct commit *c, \
int add_if_missing) \
{ \
- int nth_slab, nth_slot; \
+ unsigned int nth_slab, nth_slot; \
\
nth_slab = c->index / s->slab_size; \
nth_slot = c->index % s->slab_size; \
\
if (s->slab_count <= nth_slab) { \
- int i; \
+ unsigned int i; \
if (!add_if_missing) \
return NULL; \
REALLOC_ARRAY(s->slab, nth_slab + 1); \
pfd[i].revents = happened;
rc++;
}
+ else
+ {
+ pfd[i].revents = 0;
+ }
}
return rc;
#include "string-list.h"
#include "utf8.h"
#include "dir.h"
-#include "color.h"
struct config_source {
struct config_source *prev;
if (starts_with(var, "advice."))
return git_default_advice_config(var, value);
- if (git_color_config(var, value, dummy) < 0)
- return -1;
-
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value);
return 0;
size_t *offset;
unsigned int offset_alloc;
enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
- int seen;
+ unsigned int seen;
} store;
static int matches(const char *key, const char *value)
return 4;
}
-static int store_write_section(int fd, const char *key)
+static ssize_t write_section(int fd, const char *key)
{
const char *dot;
- int i, success;
+ int i;
+ ssize_t ret;
struct strbuf sb = STRBUF_INIT;
dot = memchr(key, '.', store.baselen);
strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
}
- success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+ ret = write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
- return success;
+ return ret;
}
-static int store_write_pair(int fd, const char *key, const char *value)
+static ssize_t write_pair(int fd, const char *key, const char *value)
{
- int i, success;
+ int i;
+ ssize_t ret;
int length = strlen(key + store.baselen + 1);
const char *quote = "";
struct strbuf sb = STRBUF_INIT;
}
strbuf_addf(&sb, "%s\n", quote);
- success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+ ret = write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
- return success;
+ return ret;
}
static ssize_t find_beginning_of_line(const char *contents, size_t size,
{
int fd = -1, in_fd = -1;
int ret;
- struct lock_file *lock = NULL;
+ static struct lock_file lock;
char *filename_buf = NULL;
char *contents = NULL;
size_t contents_sz;
* The lock serves a purpose in addition to locking: the new
* contents of .git/config will be written into it.
*/
- lock = xcalloc(1, sizeof(struct lock_file));
- fd = hold_lock_file_for_update(lock, config_filename, 0);
+ fd = hold_lock_file_for_update(&lock, config_filename, 0);
if (fd < 0) {
error_errno("could not lock config file %s", config_filename);
free(store.key);
}
store.key = (char *)key;
- if (!store_write_section(fd, key) ||
- !store_write_pair(fd, key, value))
+ if (write_section(fd, key) < 0 ||
+ write_pair(fd, key, value) < 0)
goto write_err_out;
} else {
struct stat st;
close(in_fd);
in_fd = -1;
- if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
- error_errno("chmod on %s failed", get_lock_file_path(lock));
+ if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
+ error_errno("chmod on %s failed", get_lock_file_path(&lock));
ret = CONFIG_NO_WRITE;
goto out_free;
}
/* write the first part of the config */
if (copy_end > copy_begin) {
if (write_in_full(fd, contents + copy_begin,
- copy_end - copy_begin) <
- copy_end - copy_begin)
+ copy_end - copy_begin) < 0)
goto write_err_out;
if (new_line &&
- write_str_in_full(fd, "\n") != 1)
+ write_str_in_full(fd, "\n") < 0)
goto write_err_out;
}
copy_begin = store.offset[i];
/* write the pair (value == NULL means unset) */
if (value != NULL) {
if (store.state == START) {
- if (!store_write_section(fd, key))
+ if (write_section(fd, key) < 0)
goto write_err_out;
}
- if (!store_write_pair(fd, key, value))
+ if (write_pair(fd, key, value) < 0)
goto write_err_out;
}
/* write the rest of the config */
if (copy_begin < contents_sz)
if (write_in_full(fd, contents + copy_begin,
- contents_sz - copy_begin) <
- contents_sz - copy_begin)
+ contents_sz - copy_begin) < 0)
goto write_err_out;
munmap(contents, contents_sz);
contents = NULL;
}
- if (commit_lock_file(lock) < 0) {
+ if (commit_lock_file(&lock) < 0) {
error_errno("could not write config file %s", config_filename);
ret = CONFIG_NO_WRITE;
- lock = NULL;
goto out_free;
}
- /*
- * lock is committed, so don't try to roll it back below.
- * NOTE: Since lockfile.c keeps a linked list of all created
- * lock_file structures, it isn't safe to free(lock). It's
- * better to just leave it hanging around.
- */
- lock = NULL;
ret = 0;
/* Invalidate the config cache */
git_config_clear();
out_free:
- if (lock)
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
free(filename_buf);
if (contents)
munmap(contents, contents_sz);
return ret;
write_err_out:
- ret = write_error(get_lock_file_path(lock));
+ ret = write_error(get_lock_file_path(&lock));
goto out_free;
}
continue;
}
store.baselen = strlen(new_name);
- if (!store_write_section(out_fd, new_name)) {
+ if (write_section(out_fd, new_name) < 0) {
ret = write_error(get_lock_file_path(lock));
goto out;
}
if (remove)
continue;
length = strlen(output);
- if (write_in_full(out_fd, output, length) != length) {
+ if (write_in_full(out_fd, output, length) < 0) {
ret = write_error(get_lock_file_path(lock));
goto out;
}
T *src;
expression n;
@@
-- memcpy(dst, src, n * sizeof(*dst));
+- memcpy(dst, src, (n) * sizeof(*dst));
+ COPY_ARRAY(dst, src, n);
@@
T *src;
expression n;
@@
-- memcpy(dst, src, n * sizeof(*src));
+- memcpy(dst, src, (n) * sizeof(*src));
+ COPY_ARRAY(dst, src, n);
@@
T *src;
expression n;
@@
-- memcpy(dst, src, n * sizeof(T));
+- memcpy(dst, src, (n) * sizeof(T));
+ COPY_ARRAY(dst, src, n);
@@
T *ptr;
expression n;
@@
-- ptr = xmalloc(n * sizeof(*ptr));
+- ptr = xmalloc((n) * sizeof(*ptr));
+ ALLOC_ARRAY(ptr, n);
@@
T *ptr;
expression n;
@@
-- ptr = xmalloc(n * sizeof(T));
+- ptr = xmalloc((n) * sizeof(T));
+ ALLOC_ARRAY(ptr, n);
__gitcomp "
--all --tags --contains --abbrev= --candidates=
--exact-match --debug --long --match --always --first-parent
- --exclude
+ --exclude --dirty --broken
"
return
esac
test: all
$(MAKE) -C t
+clean:
+ $(RM) diff-highlight
+
.PHONY: FORCE
ca->crlf_action = git_path_check_crlf(ccheck + 4);
if (ca->crlf_action == CRLF_UNDEFINED)
ca->crlf_action = git_path_check_crlf(ccheck + 0);
- ca->attr_action = ca->crlf_action;
ca->ident = git_path_check_ident(ccheck + 1);
ca->drv = git_path_check_convert(ccheck + 2);
if (ca->crlf_action != CRLF_BINARY) {
else if (eol_attr == EOL_CRLF)
ca->crlf_action = CRLF_TEXT_CRLF;
}
- ca->attr_action = ca->crlf_action;
} else {
ca->drv = NULL;
ca->crlf_action = CRLF_UNDEFINED;
ca->ident = 0;
}
+
+ /* Save attr and make a decision for action */
+ ca->attr_action = ca->crlf_action;
if (ca->crlf_action == CRLF_TEXT)
ca->crlf_action = text_eol_is_crlf() ? CRLF_TEXT_CRLF : CRLF_TEXT_INPUT;
if (ca->crlf_action == CRLF_UNDEFINED && auto_crlf == AUTO_CRLF_FALSE)
return 0;
}
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
return git_diff_basic_config(var, value, cb);
}
struct diff_words_buffer {
mmfile_t text;
- long alloc;
+ unsigned long alloc;
struct diff_words_orig {
const char *begin, *end;
} *orig;
blob = buf.buf;
size = buf.len;
}
- if (write_in_full(fd, blob, size) != size)
+ if (write_in_full(fd, blob, size) < 0)
die_errno("unable to write temp-file");
close_tempfile(&temp->tempfile);
temp->name = get_tempfile_path(&temp->tempfile);
char *new;
struct strbuf buf = STRBUF_INIT;
unsigned long size;
- size_t wrote, newsize = 0;
+ ssize_t wrote;
+ size_t newsize = 0;
struct stat st;
const struct submodule *sub;
fstat_done = fstat_output(fd, state, &st);
close(fd);
free(new);
- if (wrote != size)
+ if (wrote < 0)
return error("unable to write file %s", path);
break;
case S_IFGITLINK:
#define MAX_ARGS 32
static const char *argv_exec_path;
+
+#ifdef RUNTIME_PREFIX
static const char *argv0_path;
-char *system_path(const char *path)
+static const char *system_prefix(void)
{
-#ifdef RUNTIME_PREFIX
static const char *prefix;
-#else
- static const char *prefix = PREFIX;
-#endif
- struct strbuf d = STRBUF_INIT;
-
- if (is_absolute_path(path))
- return xstrdup(path);
-#ifdef RUNTIME_PREFIX
assert(argv0_path);
assert(is_absolute_path(argv0_path));
"but prefix computation failed. "
"Using static fallback '%s'.\n", prefix);
}
-#endif
-
- strbuf_addf(&d, "%s/%s", prefix, path);
- return strbuf_detach(&d, NULL);
+ return prefix;
}
void git_extract_argv0_path(const char *argv0)
argv0_path = xstrndup(argv0, slash - argv0);
}
+#else
+
+static const char *system_prefix(void)
+{
+ return PREFIX;
+}
+
+void git_extract_argv0_path(const char *argv0)
+{
+}
+
+#endif /* RUNTIME_PREFIX */
+
+char *system_path(const char *path)
+{
+ struct strbuf d = STRBUF_INIT;
+
+ if (is_absolute_path(path))
+ return xstrdup(path);
+
+ strbuf_addf(&d, "%s/%s", system_prefix(), path);
+ return strbuf_detach(&d, NULL);
+}
+
void git_set_argv_exec_path(const char *exec_path)
{
argv_exec_path = exec_path;
static void cat_blob_write(const char *buf, unsigned long size)
{
- if (write_in_full(cat_blob_fd, buf, size) != size)
+ if (write_in_full(cat_blob_fd, buf, size) < 0)
die_errno("Write to frontend failed");
}
checkpoint_requested = 0;
if (object_count) {
cycle_packfile();
- dump_branches();
- dump_tags();
- dump_marks();
}
+ dump_branches();
+ dump_tags();
+ dump_marks();
}
static void parse_checkpoint(void)
continue;
if (S_ISDIR(entry.mode)) {
- obj = &lookup_tree(entry.oid)->object;
- if (name)
+ obj = (struct object *)lookup_tree(entry.oid);
+ if (name && obj)
put_object_name(options, obj, "%s%s/", name,
entry.path);
result = options->walk(obj, OBJ_TREE, data, options);
}
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
- obj = &lookup_blob(entry.oid)->object;
- if (name)
+ obj = (struct object *)lookup_blob(entry.oid);
+ if (name && obj)
put_object_name(options, obj, "%s%s", name,
entry.path);
result = options->walk(obj, OBJ_BLOB, data, options);
static inline size_t xsize_t(off_t len)
{
- if (len > (size_t) len)
+ size_t size = (size_t) len;
+
+ if (len != (off_t) size)
die("Cannot handle files this big");
- return (size_t)len;
+ return size;
}
__attribute__((format (printf, 3, 4)))
%s (%ci)
-are available in the git repository at:
+are available in the Git repository at:
' $merge_base &&
echo " $url $pretty_remote" &&
git show -s --format='
}
my $have_email_valid = eval { require Email::Valid; 1 };
-my $have_mail_address = eval { require Mail::Address; 1 };
my $smtp;
my $auth;
my $num_sent = 0;
($repocommitter) = Git::ident_person(@repo, 'committer');
sub parse_address_line {
- if ($have_mail_address) {
- return map { $_->format } Mail::Address->parse($_[0]);
- } else {
- return Git::parse_mailboxes($_[0]);
- }
+ return Git::parse_mailboxes($_[0]);
}
sub split_addrs {
}
+sub strip_garbage_one_address {
+ my ($addr) = @_;
+ chomp $addr;
+ if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {
+ # "Foo Bar" <foobar@example.com> [possibly garbage here]
+ # Foo Bar <foobar@example.com> [possibly garbage here]
+ return $1;
+ }
+ if ($addr =~ /^(<[^>]*>).*/) {
+ # <foo@example.com> [possibly garbage here]
+ # if garbage contains other addresses, they are ignored.
+ return $1;
+ }
+ if ($addr =~ /^([^"#,\s]*)/) {
+ # address without quoting: remove anything after the address
+ return $1;
+ }
+ return $addr;
+}
+
sub sanitize_address_list {
return (map { sanitize_address($_) } @_);
}
# Now parse the message body
while(<$fh>) {
$message .= $_;
- if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
+ if (/^(Signed-off-by|Cc): (.*)/i) {
chomp;
my ($what, $c) = ($1, $2);
- chomp $c;
+ # strip garbage for the address we'll use:
+ $c = strip_garbage_one_address($c);
+ # sanitize a bit more to decide whether to suppress the address:
my $sc = sanitize_address($c);
if ($sc eq $sender) {
next if ($suppress_cc{'self'});
die("zlib error inflating request, result %d", ret);
n = stream.total_out - cnt;
- if (write_in_full(out, out_buf, n) != n)
+ if (write_in_full(out, out_buf, n) < 0)
die("%s aborted reading request", prog_name);
cnt += n;
ssize_t n = read_request(0, &buf);
if (n < 0)
die_errno("error reading request body");
- if (write_in_full(out, buf, n) != n)
+ if (write_in_full(out, buf, n) < 0)
die("%s aborted reading request", prog_name);
close(out);
free(buf);
memcpy(hex, path, 2);
path += 2;
path++; /* skip '/' */
- memcpy(hex, path, GIT_SHA1_HEXSZ - 2);
+ memcpy(hex + 2, path, GIT_SHA1_HEXSZ - 2);
return get_oid_hex(hex, oid);
}
*/
static void range_set_check_invariants(struct range_set *rs)
{
- int i;
+ unsigned int i;
if (!rs)
return;
*/
void sort_and_merge_range_set(struct range_set *rs)
{
- int i;
- int o = 0; /* output cursor */
+ unsigned int i;
+ unsigned int o = 0; /* output cursor */
QSORT(rs->ranges, rs->nr, range_cmp);
static void range_set_union(struct range_set *out,
struct range_set *a, struct range_set *b)
{
- int i = 0, j = 0;
+ unsigned int i = 0, j = 0;
struct range *ra = a->ranges;
struct range *rb = b->ranges;
/* cannot make an alias of out->ranges: it may change during grow */
static void range_set_difference(struct range_set *out,
struct range_set *a, struct range_set *b)
{
- int i, j = 0;
+ unsigned int i, j = 0;
for (i = 0; i < a->nr; i++) {
long start = a->ranges[i].start;
long end = a->ranges[i].end;
struct diff_ranges *diff,
struct range_set *rs)
{
- int i, j = 0;
+ unsigned int i, j = 0;
assert(out->target.nr == 0);
struct range_set *rs,
struct diff_ranges *diff)
{
- int i, j = 0;
+ unsigned int i, j = 0;
long offset = 0;
struct range *src = rs->ranges;
struct range *target = diff->target.ranges;
static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
{
- int i, j = 0;
+ unsigned int i, j = 0;
long p_lines, t_lines;
unsigned long *p_ends = NULL, *t_ends = NULL;
struct diff_filepair *pair = range->pair;
long t_start = range->ranges.ranges[i].start;
long t_end = range->ranges.ranges[i].end;
long t_cur = t_start;
- int j_last;
+ unsigned int j_last;
while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
j++;
/* A set of ranges. The ranges must always be disjoint and sorted. */
struct range_set {
- int alloc, nr;
+ unsigned int alloc, nr;
struct range *ranges;
};
xsnprintf(path, len, ".merge_file_XXXXXX");
fd = xmkstemp(path);
- if (write_in_full(fd, src->ptr, src->size) != src->size)
+ if (write_in_full(fd, src->ptr, src->size) < 0)
die_errno("unable to write temp-file");
close(fd);
}
while ((c = *in++) != 0) {
if (c == '=') {
- int d = *in++;
+ int ch, d = *in;
if (d == '\n' || !d)
break; /* drop trailing newline */
- strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
- continue;
+ ch = hex2chr(in);
+ if (ch >= 0) {
+ strbuf_addch(out, ch);
+ in += 2;
+ continue;
+ }
+ /* garbage -- fall through */
}
if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
c = 0x20;
fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
while (size > 0) {
- long ret = write_in_full(fd, buf, size);
+ ssize_t ret = write_in_full(fd, buf, size);
if (ret < 0) {
/* Ignore epipe */
if (errno == EPIPE)
return sb;
}
-static char *cleanup_path(char *path)
+static const char *cleanup_path(const char *path)
{
/* Clean it up */
- if (!memcmp(path, "./", 2)) {
- path += 2;
+ if (skip_prefix(path, "./", &path)) {
while (*path == '/')
path++;
}
static void strbuf_cleanup_path(struct strbuf *sb)
{
- char *path = cleanup_path(sb->buf);
+ const char *path = cleanup_path(sb->buf);
if (path > sb->buf)
strbuf_remove(sb, 0, path - sb->buf);
}
strlcpy(buf, bad_path, n);
return buf;
}
- return cleanup_path(buf);
+ return (char *)cleanup_path(buf);
}
static int dir_prefix(const char *buf, const char *dir)
int validate_headref(const char *path)
{
struct stat st;
- char *buf, buffer[256];
- unsigned char sha1[20];
+ char buffer[256];
+ const char *refname;
+ struct object_id oid;
int fd;
ssize_t len;
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
+ if (len < 0)
+ return -1;
+ buffer[len] = '\0';
+
/*
* Is it a symbolic ref?
*/
- if (len < 4)
- return -1;
- if (!memcmp("ref:", buffer, 4)) {
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- if (len >= 5 && !memcmp("refs/", buf, 5))
+ if (skip_prefix(buffer, "ref:", &refname)) {
+ while (isspace(*refname))
+ refname++;
+ if (starts_with(refname, "refs/"))
return 0;
}
/*
* Is this a detached HEAD?
*/
- if (!get_sha1_hex(buffer, sha1))
+ if (!get_oid_hex(buffer, &oid))
return 0;
return -1;
int packet_flush_gently(int fd)
{
packet_trace("0000", 4, 1);
- if (write_in_full(fd, "0000", 4) == 4)
- return 0;
- return error("flush packet write failed");
+ if (write_in_full(fd, "0000", 4) < 0)
+ return error("flush packet write failed");
+ return 0;
}
void packet_buf_flush(struct strbuf *buf)
static int packet_write_fmt_1(int fd, int gently,
const char *fmt, va_list args)
{
- struct strbuf buf = STRBUF_INIT;
- ssize_t count;
+ static struct strbuf buf = STRBUF_INIT;
+ strbuf_reset(&buf);
format_packet(&buf, fmt, args);
- count = write_in_full(fd, buf.buf, buf.len);
- if (count == buf.len)
- return 0;
-
- if (!gently) {
- check_pipe(errno);
- die_errno("packet write with format failed");
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
+ if (!gently) {
+ check_pipe(errno);
+ die_errno("packet write with format failed");
+ }
+ return error("packet write with format failed");
}
- return error("packet write with format failed");
+
+ return 0;
}
void packet_write_fmt(int fd, const char *fmt, ...)
packet_size = size + 4;
set_packet_header(packet_write_buffer, packet_size);
memcpy(packet_write_buffer + 4, buf, size);
- if (write_in_full(fd_out, packet_write_buffer, packet_size) == packet_size)
- return 0;
- return error("packet write failed");
+ if (write_in_full(fd_out, packet_write_buffer, packet_size) < 0)
+ return error("packet write failed");
+ return 0;
}
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
unsigned int buffered = write_buffer_len;
if (buffered) {
git_SHA1_Update(context, write_buffer, buffered);
- if (write_in_full(fd, write_buffer, buffered) != buffered)
+ if (write_in_full(fd, write_buffer, buffered) < 0)
return -1;
write_buffer_len = 0;
}
/* Flush first if not enough space for SHA1 signature */
if (left + 20 > WRITE_BUFFER_SIZE) {
- if (write_in_full(fd, write_buffer, left) != left)
+ if (write_in_full(fd, write_buffer, left) < 0)
return -1;
left = 0;
}
git_SHA1_Final(write_buffer + left, context);
hashcpy(sha1, write_buffer + left);
left += 20;
- return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
+ return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
}
static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
REALLOC_ARRAY(used_atom, used_atom_cnt);
used_atom[at].name = xmemdupz(atom, ep - atom);
used_atom[at].type = valid_atom[i].cmp_type;
- if (arg)
+ if (arg) {
arg = used_atom[at].name + (arg - atom) + 1;
+ if (!*arg) {
+ /*
+ * Treat empty sub-arguments list as NULL (i.e.,
+ * "%(atom:)" is equivalent to "%(atom)").
+ */
+ arg = NULL;
+ }
+ }
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser)
valid_atom[i].parser(format, &used_atom[at], arg);
}
}
- if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
strbuf_addf(err, "could not write to '%s'", filename);
rollback_lock_file(&lock);
goto done;
return -1;
}
+ flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
+
flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
ref_transaction_add_update(transaction, refname, flags,
#define REF_FORCE_CREATE_REFLOG 0x40
/*
+ * Flags that can be passed in to ref_transaction_update
+ */
+#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
+ REF_ISPRUNING | \
+ REF_FORCE_CREATE_REFLOG | \
+ REF_NODEREF
+
+/*
* Setup reflog before using. Fill in err and return -1 on failure.
*/
int refs_create_reflog(struct ref_store *refs, const char *refname,
written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
free(logrec);
- if (written != len)
+ if (written < 0)
return -1;
return 0;
return -1;
}
fd = get_lock_file_fd(lock->lk);
- if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
- write_in_full(fd, &term, 1) != 1 ||
+ if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 ||
+ write_in_full(fd, &term, 1) < 0 ||
close_ref(lock) < 0) {
strbuf_addf(err,
"couldn't write '%s'", get_lock_file_path(lock->lk));
strerror(errno));
} else if (update &&
(write_in_full(get_lock_file_fd(lock->lk),
- oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
- write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
+ oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) < 0 ||
+ write_str_in_full(get_lock_file_fd(lock->lk), "\n") < 0 ||
close_ref(lock) < 0)) {
status |= error("couldn't write %s",
get_lock_file_path(lock->lk));
rerere_id_hex(id),
rr->items[i].string, 0);
- if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
+ if (write_in_full(out_fd, buf.buf, buf.len) < 0)
die("unable to write rerere record");
strbuf_release(&buf);
unsigned flags)
{
struct rev_cmdline_info *info = &revs->cmdline;
- int nr = info->nr;
+ unsigned int nr = info->nr;
ALLOC_GROW(info->rev, nr + 1, info->alloc);
info->rev[nr].item = item;
}
/* Create an array of 'char *' to be used as the childenv */
- childenv = xmalloc((env.nr + 1) * sizeof(char *));
+ ALLOC_ARRAY(childenv, env.nr + 1);
for (i = 0; i < env.nr; i++)
childenv[i] = env.items[i].util;
childenv[env.nr] = NULL;
} while (lo < hi);
return -lo-1;
}
-
-/*
- * Conventional binary search loop looks like this:
- *
- * unsigned lo, hi;
- * do {
- * unsigned mi = (lo + hi) / 2;
- * int cmp = "entry pointed at by mi" minus "target";
- * if (!cmp)
- * return (mi is the wanted one)
- * if (cmp > 0)
- * hi = mi; "mi is larger than target"
- * else
- * lo = mi+1; "mi is smaller than target"
- * } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- * above the target (it could be at the target), hi points at a
- * slot that is guaranteed to be above the target (it can never
- * be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- * as lo, but never can be as same as hi), and check if it hits
- * the target. There are three cases:
- *
- * - if it is a hit, we are happy.
- *
- * - if it is strictly higher than the target, we set it to hi,
- * and repeat the search.
- *
- * - if it is strictly lower than the target, we update lo to
- * one slot after it, because we allow lo to be at the target.
- *
- * If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied. When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution. An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table. It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely. If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search. So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each. Each entry has the SHA-1 key at "key_offset". The
- * table is sorted by the SHA-1 key of the entries. The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key)
-{
- const unsigned char *base = table;
- const unsigned char *hi_key, *lo_key;
- unsigned ofs_0;
- static int debug_lookup = -1;
-
- if (debug_lookup < 0)
- debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
-
- if (!nr || lo >= hi)
- return -1;
-
- if (nr == hi)
- hi_key = NULL;
- else
- hi_key = base + elem_size * hi + key_offset;
- lo_key = base + elem_size * lo + key_offset;
-
- ofs_0 = 0;
- do {
- int cmp;
- unsigned ofs, mi, range;
- unsigned lov, hiv, kyv;
- const unsigned char *mi_key;
-
- range = hi - lo;
- if (hi_key) {
- for (ofs = ofs_0; ofs < 20; ofs++)
- if (lo_key[ofs] != hi_key[ofs])
- break;
- ofs_0 = ofs;
- /*
- * byte 0 thru (ofs-1) are the same between
- * lo and hi; ofs is the first byte that is
- * different.
- *
- * If ofs==20, then no bytes are different,
- * meaning we have entries with duplicate
- * keys. We know that we are in a solid run
- * of this entry (because the entries are
- * sorted, and our lo and hi are the same,
- * there can be nothing but this single key
- * in between). So we can stop the search.
- * Either one of these entries is it (and
- * we do not care which), or we do not have
- * it.
- *
- * Furthermore, we know that one of our
- * endpoints must be the edge of the run of
- * duplicates. For example, given this
- * sequence:
- *
- * idx 0 1 2 3 4 5
- * key A C C C C D
- *
- * If we are searching for "B", we might
- * hit the duplicate run at lo=1, hi=3
- * (e.g., by first mi=3, then mi=0). But we
- * can never have lo > 1, because B < C.
- * That is, if our key is less than the
- * run, we know that "lo" is the edge, but
- * we can say nothing of "hi". Similarly,
- * if our key is greater than the run, we
- * know that "hi" is the edge, but we can
- * say nothing of "lo".
- *
- * Therefore if we do not find it, we also
- * know where it would go if it did exist:
- * just on the far side of the edge that we
- * know about.
- */
- if (ofs == 20) {
- mi = lo;
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key, key, 20);
- if (!cmp)
- return mi;
- if (cmp < 0)
- return -1 - hi;
- else
- return -1 - lo;
- }
-
- hiv = hi_key[ofs_0];
- if (ofs_0 < 19)
- hiv = (hiv << 8) | hi_key[ofs_0+1];
- } else {
- hiv = 256;
- if (ofs_0 < 19)
- hiv <<= 8;
- }
- lov = lo_key[ofs_0];
- kyv = key[ofs_0];
- if (ofs_0 < 19) {
- lov = (lov << 8) | lo_key[ofs_0+1];
- kyv = (kyv << 8) | key[ofs_0+1];
- }
- assert(lov < hiv);
-
- if (kyv < lov)
- return -1 - lo;
- if (hiv < kyv)
- return -1 - hi;
-
- /*
- * Even if we know the target is much closer to 'hi'
- * than 'lo', if we pick too precisely and overshoot
- * (e.g. when we know 'mi' is closer to 'hi' than to
- * 'lo', pick 'mi' that is higher than the target), we
- * end up narrowing the search space by a smaller
- * amount (i.e. the distance between 'mi' and 'hi')
- * than what we would have (i.e. about half of 'lo'
- * and 'hi'). Hedge our bets to pick 'mi' less
- * aggressively, i.e. make 'mi' a bit closer to the
- * middle than we would otherwise pick.
- */
- kyv = (kyv * 6 + lov + hiv) / 8;
- if (lov < hiv - 1) {
- if (kyv == lov)
- kyv++;
- else if (kyv == hiv)
- kyv--;
- }
- mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
- if (debug_lookup) {
- printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
- printf("ofs %u lov %x, hiv %x, kyv %x\n",
- ofs_0, lov, hiv, kyv);
- }
- if (!(lo <= mi && mi < hi))
- die("assertion failure lo %u mi %u hi %u %s",
- lo, mi, hi, sha1_to_hex(key));
-
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
- if (!cmp)
- return mi;
- if (cmp > 0) {
- hi = mi;
- hi_key = mi_key;
- } else {
- lo = mi + 1;
- lo_key = mi_key + elem_size;
- }
- } while (lo < hi);
- return -lo-1;
-}
void *table,
size_t nr,
sha1_access_fn fn);
-
-extern int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key);
#endif
return end;
}
-static void link_alt_odb_entries(const char *alt, int len, int sep,
+static void link_alt_odb_entries(const char *alt, int sep,
const char *relative_base, int depth)
{
struct strbuf objdirbuf = STRBUF_INIT;
static void read_info_alternates(const char * relative_base, int depth)
{
- char *map;
- size_t mapsz;
- struct stat st;
char *path;
- int fd;
+ struct strbuf buf = STRBUF_INIT;
path = xstrfmt("%s/info/alternates", relative_base);
- fd = git_open(path);
- free(path);
- if (fd < 0)
- return;
- if (fstat(fd, &st) || (st.st_size == 0)) {
- close(fd);
+ if (strbuf_read_file(&buf, path, 1024) < 0) {
+ warn_on_fopen_errors(path);
+ free(path);
return;
}
- mapsz = xsize_t(st.st_size);
- map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
-
- link_alt_odb_entries(map, mapsz, '\n', relative_base, depth);
- munmap(map, mapsz);
+ link_alt_odb_entries(buf.buf, '\n', relative_base, depth);
+ strbuf_release(&buf);
+ free(path);
}
struct alternate_object_database *alloc_alt_odb(const char *dir)
if (commit_lock_file(lock))
die_errno("unable to move new alternates file into place");
if (alt_odb_tail)
- link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+ link_alt_odb_entries(reference, '\n', NULL, 0);
}
free(alts);
}
*/
prepare_alt_odb();
- link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+ link_alt_odb_entries(reference, '\n', NULL, 0);
}
/*
if (!alt) alt = "";
alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
+ link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
read_info_alternates(get_object_directory(), 0);
}
const uint32_t *level1_ofs = p->index_data;
const unsigned char *index = p->index_data;
unsigned hi, lo, stride;
- static int use_lookup = -1;
static int debug_lookup = -1;
if (debug_lookup < 0)
printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
- if (use_lookup < 0)
- use_lookup = !!getenv("GIT_USE_LOOKUP");
- if (use_lookup) {
- int pos = sha1_entry_pos(index, stride, 0,
- lo, hi, p->num_objects, sha1);
- if (pos < 0)
- return 0;
- return nth_packed_object_offset(p, pos);
- }
-
while (lo < hi) {
unsigned mi = (lo + hi) / 2;
int cmp = hashcmp(index + mi * stride, sha1);
} else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
status = error("unable to parse %s header", sha1_to_hex(sha1));
- if (status >= 0 && oi->contentp)
+ if (status >= 0 && oi->contentp) {
*oi->contentp = unpack_sha1_rest(&stream, hdr,
*oi->sizep, sha1);
- else
+ if (!*oi->contentp) {
+ git_inflate_end(&stream);
+ status = -1;
+ }
+ } else
git_inflate_end(&stream);
munmap(map, mapsize);
int read_pack_header(int fd, struct pack_header *header)
{
- if (read_in_full(fd, header, sizeof(*header)) < sizeof(*header))
+ if (read_in_full(fd, header, sizeof(*header)) != sizeof(*header))
/* "eof before pack header was fully read" */
return PH_ERROR_EOF;
if (write_shallow_commits(&sb, 0, extra)) {
fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
- if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+ if (write_in_full(fd, sb.buf, sb.len) < 0)
die_errno("failed to write to %s",
get_tempfile_path(&temporary_shallow));
close_tempfile(&temporary_shallow);
LOCK_DIE_ON_ERROR);
check_shallow_file_for_update();
if (write_shallow_commits(&sb, 0, extra)) {
- if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+ if (write_in_full(fd, sb.buf, sb.len) < 0)
die_errno("failed to write to %s",
get_lock_file_path(shallow_lock));
*alternate_shallow_file = get_lock_file_path(shallow_lock);
LOCK_DIE_ON_ERROR);
check_shallow_file_for_update();
if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
- if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+ if (write_in_full(fd, sb.buf, sb.len) < 0)
die_errno("failed to write to %s",
get_lock_file_path(&shallow_lock));
commit_lock_file(&shallow_lock);
/* Translate slopbuf to NULL, as we cannot call realloc on it */
if (!sb->alloc)
sb->buf = NULL;
+ errno = 0;
r = getdelim(&sb->buf, &sb->alloc, term, fp);
if (r > 0) {
extern void strbuf_init(struct strbuf *, size_t);
/**
- * Release a string buffer and the memory it used. You should not use the
- * string buffer after using this function, unless you initialize it again.
+ * Release a string buffer and the memory it used. After this call, the
+ * strbuf points to an empty string that does not need to be free()ed, as
+ * if it had been set to `STRBUF_INIT` and never modified.
+ *
+ * To clear a strbuf in preparation for further use without the overhead
+ * of free()ing and malloc()ing again, use strbuf_reset() instead.
*/
extern void strbuf_release(struct strbuf *);
* Detach the string from the strbuf and returns it; you now own the
* storage the string occupies and it is your responsibility from then on
* to release it with `free(3)` when you are done with it.
+ *
+ * The strbuf that previously held the string is reset to `STRBUF_INIT` so
+ * it can be reused after calling this function.
*/
extern char *strbuf_detach(struct strbuf *, size_t *);
if (len > (sb->alloc ? sb->alloc - 1 : 0))
die("BUG: strbuf_setlen() beyond buffer");
sb->len = len;
- sb->buf[len] = '\0';
+ if (sb->buf != strbuf_slopbuf)
+ sb->buf[len] = '\0';
+ else
+ assert(!strbuf_slopbuf[0]);
}
/**
kept = 0;
wrote = write_in_full(fd, buf, readlen);
- if (wrote != readlen)
+ if (wrote < 0)
goto close_and_exit;
}
if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
int for_each_string_list(struct string_list *list,
string_list_each_func_t, void *cb_data);
-#define for_each_string_list_item(item,list) \
- for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
+#define for_each_string_list_item(item,list) \
+ for (item = (list)->items; \
+ item && item < (list)->items + (list)->nr; \
+ ++item)
/*
* Apply want to each item in list, retaining only the ones for which
{
int err;
struct child_process *process;
- const char *argv[] = { cmd, NULL };
entry->cmd = cmd;
process = &entry->process;
child_process_init(process);
- process->argv = argv;
+ argv_array_push(&process->args, cmd);
process->use_shell = 1;
process->in = -1;
process->out = -1;
if (supported_capabilities)
*supported_capabilities |= capabilities[i].flag;
} else {
- warning("subprocess '%s' requested unsupported capability '%s'",
- process->argv[0], p);
+ die("subprocess '%s' requested unsupported capability '%s'",
+ process->argv[0], p);
}
}
/*
* Prepare the "env_array" parameter of a "struct child_process" for executing
- * a submodule by clearing any repo-specific envirionment variables, but
+ * a submodule by clearing any repo-specific environment variables, but
* retaining any config in the environment.
*/
extern void prepare_submodule_repo_env(struct argv_array *out);
$ sh ./t9200-git-cvsexport-commit.sh --run='-3 21'
-As noted above, the test set is built going though items left to
-right, so this:
+As noted above, the test set is built by going through the items
+from left to right, so this:
$ sh ./t9200-git-cvsexport-commit.sh --run='1-4 !3'
-will run tests 1, 2, and 4. Items that comes later have higher
+will run tests 1, 2, and 4. Items that come later have higher
precedence. It means that this:
$ sh ./t9200-git-cvsexport-commit.sh --run='!3 1-4'
while (<>) {
chomp;
/\bsed\s+-i/ and err 'sed -i is not portable';
- /\becho\s+-n/ and err 'echo -n is not portable (please use printf)';
+ /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)';
/^\s*declare\s+/ and err 'arrays/declare not portable';
/^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
/\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
}
fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
- if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
+ if (fd < 0 || write_in_full(fd, out_buf, out_size) < 0) {
perror(argv[4]);
return 1;
}
* Split by newline, but don't create a string_list item
* for the empty string after the last separator.
*/
- if (sb.buf[sb.len - 1] == '\n')
+ if (sb.len && sb.buf[sb.len - 1] == '\n')
strbuf_setlen(&sb, sb.len - 1);
string_list_split_in_place(&list, sb.buf, '\n', -1);
test_i18ngrep ! "fatal: empty filename in tree entry" out
'
+hex2oct() {
+ perl -ne 'printf "\\%03o", hex for /../g'
+}
+
+test_expect_success 'tree entry with type mismatch' '
+ test_when_finished "remove_object \$blob" &&
+ test_when_finished "remove_object \$tree" &&
+ test_when_finished "remove_object \$commit" &&
+ test_when_finished "git update-ref -d refs/heads/type_mismatch" &&
+ blob=$(echo blob | git hash-object -w --stdin) &&
+ blob_bin=$(echo $blob | hex2oct) &&
+ tree=$(
+ printf "40000 dir\0${blob_bin}100644 file\0${blob_bin}" |
+ git hash-object -t tree --stdin -w --literally
+ ) &&
+ commit=$(git commit-tree $tree) &&
+ git update-ref refs/heads/type_mismatch $commit &&
+ test_must_fail git fsck >out 2>&1 &&
+ test_i18ngrep "is a blob, not a tree" out &&
+ test_i18ngrep ! "dangling blob" out
+'
+
test_expect_success 'tag pointing to nonexistent' '
cat >invalid-tag <<-\EOF &&
object ffffffffffffffffffffffffffffffffffffffff
grep "^0\{40\}.*$msg$" .git/logs/HEAD
'
+test_expect_success 'git branch -M should leave orphaned HEAD alone' '
+ git init orphan &&
+ (
+ cd orphan &&
+ test_commit initial &&
+ git checkout --orphan lonely &&
+ grep lonely .git/HEAD &&
+ test_path_is_missing .git/refs/head/lonely &&
+ git branch -M master mistress &&
+ grep lonely .git/HEAD
+ )
+'
+
test_expect_success 'resulting reflog can be shown by log -g' '
oid=$(git rev-parse HEAD) &&
cat >expect <<-EOF &&
'
test_expect_success TTY '%(color) present with tty' '
- test_terminal env TERM=vt100 git branch $color_args >actual.raw &&
- test_decode_color <actual.raw >actual &&
- test_cmp expect.color actual
-'
-
-test_expect_success 'color.branch=always overrides auto-color' '
- git -c color.branch=always branch $color_args >actual.raw &&
+ test_terminal git branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
# choose non-default colors to make sure config
# is taking effect
test_expect_success 'set up some color config' '
- git config color.branch always &&
git config color.branch.local blue &&
git config color.branch.remote yellow &&
git config color.branch.current cyan
<BLUE>other<RESET>
<YELLOW>remotes/origin/master<RESET>
EOF
- git branch -a >actual.raw &&
+ git branch --color -a >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect actual
'
<BLUE>other <RESET> $oid foo
<YELLOW>remotes/origin/master<RESET> $oid foo
EOF
- git branch -v -a >actual.raw &&
+ git branch --color -v -a >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect actual
'
test_description='add -i basic tests'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
if ! test_have_prereq PERL
then
test_cmp expected diff
'
-test_expect_success 'diffs can be colorized' '
+test_expect_success TTY 'diffs can be colorized' '
git reset --hard &&
- # force color even though the test script has no terminal
- test_config color.ui always &&
-
echo content >test &&
- printf y | git add -p >output 2>&1 &&
+ printf y | test_terminal git add -p >output 2>&1 &&
# We do not want to depend on the exact coloring scheme
# git uses for diffs, so just check that we saw some kind of color.
git diff --exit-code
'
+test_expect_success 'add -p works even with color.ui=always' '
+ git reset --hard &&
+ echo change >>file &&
+ test_config color.ui always &&
+ echo y | git add -p &&
+ echo file >expect &&
+ git diff --cached --name-only >actual &&
+ test_cmp expect actual
+'
+
test_done
echo "test" >x &&
git commit -m "initial" x &&
echo "{NTN}" | tr "NT" "\n\t" >>x &&
- git -c color.diff=always diff | test_decode_color >current &&
+ git diff --color | test_decode_color >current &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/x b/x<RESET>
echo "2. and a new line "
} >x &&
- git -c color.diff=always diff |
+ git diff --color |
test_decode_color >current &&
cat >expected <<-\EOF &&
test_expect_success 'test --ws-error-highlight option' '
- git -c color.diff=always diff --ws-error-highlight=default,old |
+ git diff --color --ws-error-highlight=default,old |
test_decode_color >current &&
test_cmp expect.default-old current &&
- git -c color.diff=always diff --ws-error-highlight=all |
+ git diff --color --ws-error-highlight=all |
test_decode_color >current &&
test_cmp expect.all current &&
- git -c color.diff=always diff --ws-error-highlight=none |
+ git diff --color --ws-error-highlight=none |
test_decode_color >current &&
test_cmp expect.none current
test_expect_success 'test diff.wsErrorHighlight config' '
- git -c color.diff=always -c diff.wsErrorHighlight=default,old diff |
+ git -c diff.wsErrorHighlight=default,old diff --color |
test_decode_color >current &&
test_cmp expect.default-old current &&
- git -c color.diff=always -c diff.wsErrorHighlight=all diff |
+ git -c diff.wsErrorHighlight=all diff --color |
test_decode_color >current &&
test_cmp expect.all current &&
- git -c color.diff=always -c diff.wsErrorHighlight=none diff |
+ git -c diff.wsErrorHighlight=none diff --color |
test_decode_color >current &&
test_cmp expect.none current
test_expect_success 'option overrides diff.wsErrorHighlight' '
- git -c color.diff=always -c diff.wsErrorHighlight=none \
- diff --ws-error-highlight=default,old |
+ git -c diff.wsErrorHighlight=none \
+ diff --color --ws-error-highlight=default,old |
test_decode_color >current &&
test_cmp expect.default-old current &&
- git -c color.diff=always -c diff.wsErrorHighlight=default \
- diff --ws-error-highlight=all |
+ git -c diff.wsErrorHighlight=default \
+ diff --color --ws-error-highlight=all |
test_decode_color >current &&
test_cmp expect.all current &&
- git -c color.diff=always -c diff.wsErrorHighlight=all \
- diff --ws-error-highlight=none |
+ git -c diff.wsErrorHighlight=all \
+ diff --color --ws-error-highlight=none |
test_decode_color >current &&
test_cmp expect.none current
'
test_expect_success TTY 'log output on a TTY' '
- git log --oneline --decorate >expect.short &&
+ git log --color --oneline --decorate >expect.short &&
test_terminal git log --oneline >actual &&
test_cmp expect.short actual
test_expect_missing archive-pathspec/ignored-by-tree.d
test_expect_missing archive-pathspec/ignored-by-tree.d/file
test_expect_exists archive-pathspec/ignored-by-worktree
-test_expect_missing archive-pathspec/excluded-by-pathspec.d failure
+test_expect_missing archive-pathspec/excluded-by-pathspec.d
test_expect_missing archive-pathspec/excluded-by-pathspec.d/file
test_expect_success 'git archive with wildcard pathspec' '
test_expect_missing archive/deep/and/slashless/foo &&
test_expect_missing archive/deep/with/wildcard/ &&
test_expect_missing archive/deep/with/wildcard/foo &&
-test_expect_exists archive/one-level-lower/
+test_expect_missing archive/one-level-lower/
test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
git archive --format=tar $root_tree >subtree-all.tar &&
make_dir extract &&
"$TAR" xf subtree-all.tar -C extract &&
- check_dir extract sub
+ check_dir extract
'
test_expect_success 'archive empty subtree by direct pathspec' '
git archive --format=tar $root_tree -- sub >subtree-path.tar &&
make_dir extract &&
"$TAR" xf subtree-path.tar -C extract &&
- check_dir extract sub
+ check_dir extract
'
ZIPINFO=zipinfo
cat <<-\EOT >read-request.sed &&
#!/bin/sed -nf
# Note that a request could ask for "tag $tagname"
- / in the git repository at:$/!d
+ / in the Git repository at:$/!d
n
/^$/ n
s/ tag \([^ ]*\)$/ tag--\1/
SUBJECT (DATE)
- are available in the git repository at:
+ are available in the Git repository at:
URL BRANCH
EOF
'
-test_expect_success 'lookup in duplicated pack (binary search)' '
+test_expect_success 'lookup in duplicated pack' '
git cat-file --batch-check <input >actual &&
test_cmp expect actual
'
-test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' '
- (
- GIT_USE_LOOKUP=1 &&
- export GIT_USE_LOOKUP &&
- git cat-file --batch-check <input >actual
- ) &&
- test_cmp expect actual
-'
-
test_expect_success 'index-pack can reject packs with duplicates' '
clear_packs &&
create_pack dups.pack 2 &&
#
#
-test_expect_success '--bisect can default to good/bad refs' '
+test_expect_success 'set up fake --bisect refs' '
git update-ref refs/bisect/bad c3 &&
good=$(git rev-parse b1) &&
git update-ref refs/bisect/good-$good $good &&
good=$(git rev-parse c1) &&
- git update-ref refs/bisect/good-$good $good &&
+ git update-ref refs/bisect/good-$good $good
+'
+test_expect_success 'rev-list --bisect can default to good/bad refs' '
# the only thing between c3 and c1 is c2
git rev-parse c2 >expect &&
git rev-list --bisect >actual &&
test_cmp expect actual
'
+test_expect_success 'rev-parse --bisect can default to good/bad refs' '
+ git rev-parse c3 ^b1 ^c1 >expect &&
+ git rev-parse --bisect >actual &&
+
+ # output order depends on the refnames, which in turn depends on
+ # the exact sha1s. We just want to make sure we have the same set
+ # of lines in any order.
+ sort <expect >expect.sorted &&
+ sort <actual >actual.sorted &&
+ test_cmp expect.sorted actual.sorted
+'
+
test_done
'
test_expect_success TTY "$desc respects --color=auto (stdout is tty)" '
- test_terminal env TERM=vt100 \
- git log --format=$color -1 --color=auto >actual &&
+ test_terminal git log --format=$color -1 --color=auto >actual &&
has_color actual
'
check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
-check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
+check_describe "test2-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^
+check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test3-*" HEAD
+
+check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
+
test_expect_success 'name-rev with exact tags' '
echo A >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
test_cmp expect actual
'
-test_expect_success 'exclude only no longer errors out' '
+test_expect_success 'exclude only pathspec uses default implicit pathspec' '
git log --oneline --format=%s -- . ":(exclude)sub" >expect &&
git log --oneline --format=%s -- ":(exclude)sub" >actual &&
test_cmp expect actual
test_cmp expect actual
'
+test_expect_success 'multiple exclusions' '
+ git ls-files -- ":^*/file2" ":^sub2" >actual &&
+ cat <<-\EOF >expect &&
+ file
+ sub/file
+ sub/sub/file
+ sub/sub/sub/file
+ EOF
+ test_cmp expect actual
+'
+
test_done
}
test_atom head refname refs/heads/master
+test_atom head refname: refs/heads/master
test_atom head refname:short master
test_atom head refname:lstrip=1 heads/master
test_atom head refname:lstrip=2 master
'
test_expect_success TTY '%(color) shows color with a tty' '
- test_terminal env TERM=vt100 \
- git for-each-ref --format="$color_format" >actual.raw &&
+ test_terminal git for-each-ref --format="$color_format" >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expected.color actual
'
test_cmp expected.bare actual
'
-test_expect_success 'color.ui=always can override tty check' '
- git -c color.ui=always for-each-ref --format="$color_format" >actual.raw &&
+test_expect_success '--color can override tty check' '
+ git for-each-ref --color --format="$color_format" >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expected.color actual
'
+test_expect_success 'color.ui=always does not override tty check' '
+ git -c color.ui=always for-each-ref --format="$color_format" >actual &&
+ test_cmp expected.bare actual
+'
+
cat >expected <<\EOF
heads/master
tags/master
'
test_expect_success TTY '%(color) present with tty' '
- test_terminal env TERM=vt100 git tag $color_args >actual.raw &&
+ test_terminal git tag $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
+test_expect_success '--color overrides auto-color' '
+ git tag --color $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success TTY 'color when writing to a pager' '
rm -f paginated.out &&
test_config color.ui auto &&
- test_terminal env TERM=vt100 git log &&
+ test_terminal git log &&
colorful paginated.out
'
rm -f paginated.out &&
test_config color.ui auto &&
test_config color.pager false &&
- test_terminal env TERM=vt100 git log &&
+ test_terminal git log &&
! colorful paginated.out
'
test_expect_success TTY 'colors are sent to pager for external commands' '
test_config alias.externallog "!git log" &&
test_config color.ui auto &&
- test_terminal env TERM=vt100 git -p externallog &&
+ test_terminal git -p externallog &&
colorful paginated.out
'
test_expect_success 'verbose respects diff config' '
- test_config color.diff always &&
+ test_config diff.noprefix true &&
git status -v >actual &&
- grep "\[1mdiff --git" actual
+ grep "diff --git negative negative" actual
'
mesg_with_comment_and_newlines='
test_description='git status'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'status -h in broken repository' '
git config --global advice.statusuoption false &&
'
-test_expect_success 'status with color.ui' '
+test_expect_success TTY 'status with color.ui' '
cat >expect <<\EOF &&
On branch <GREEN>master<RESET>
Your branch and '\''upstream'\'' have diverged,
<BLUE>untracked<RESET>
EOF
- test_config color.ui always &&
- git status | test_decode_color >output &&
+ test_config color.ui auto &&
+ test_terminal git status | test_decode_color >output &&
test_i18ncmp expect output
'
-test_expect_success 'status with color.status' '
- test_config color.status always &&
- git status | test_decode_color >output &&
+test_expect_success TTY 'status with color.status' '
+ test_config color.status auto &&
+ test_terminal git status | test_decode_color >output &&
test_i18ncmp expect output
'
<BLUE>??<RESET> untracked
EOF
-test_expect_success 'status -s with color.ui' '
+test_expect_success TTY 'status -s with color.ui' '
- git config color.ui always &&
- git status -s | test_decode_color >output &&
+ git config color.ui auto &&
+ test_terminal git status -s | test_decode_color >output &&
test_cmp expect output
'
-test_expect_success 'status -s with color.status' '
+test_expect_success TTY 'status -s with color.status' '
git config --unset color.ui &&
- git config color.status always &&
- git status -s | test_decode_color >output &&
+ git config color.status auto &&
+ test_terminal git status -s | test_decode_color >output &&
test_cmp expect output
'
<BLUE>??<RESET> untracked
EOF
-test_expect_success 'status -s -b with color.status' '
+test_expect_success TTY 'status -s -b with color.status' '
- git status -s -b | test_decode_color >output &&
+ test_terminal git status -s -b | test_decode_color >output &&
test_i18ncmp expect output
'
?? untracked
EOF
-test_expect_success 'status --porcelain ignores color.ui' '
+test_expect_success TTY 'status --porcelain ignores color.ui' '
git config --unset color.status &&
- git config color.ui always &&
- git status --porcelain | test_decode_color >output &&
+ git config color.ui auto &&
+ test_terminal git status --porcelain | test_decode_color >output &&
test_cmp expect output
'
-test_expect_success 'status --porcelain ignores color.status' '
+test_expect_success TTY 'status --porcelain ignores color.status' '
git config --unset color.ui &&
- git config color.status always &&
- git status --porcelain | test_decode_color >output &&
+ git config color.status auto &&
+ test_terminal git status --porcelain | test_decode_color >output &&
test_cmp expect output
'
grep "path.*needs.*filters" err
'
+test_expect_success '--textconv/--filters complain without path' '
+ test_must_fail git cat-file --textconv HEAD &&
+ test_must_fail git cat-file --filters HEAD
+'
+
test_expect_success 'cat-file --textconv --batch works' '
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
!two@example.com!
!three@example.com!
!four@example.com!
+!five@example.com!
+!six@example.com!
EOF
"
Cc: <two@example.com> # trailing comments are ignored
Cc: <three@example.com>, <not.four@example.com> one address per line
Cc: "Some # Body" <four@example.com> [ <also.a.comment> ]
+ Cc: five@example.com # not.six@example.com
+ Cc: six@example.com, not.seven@example.com
EOF
clean_fake_sendmail &&
git send-email -1 --to=recipient@example.com \
compare_diff_raw expect actual
'
+###
+### series V (checkpoint)
+###
+
+# The commands in input_file should not produce any output on the file
+# descriptor set with --cat-blob-fd (or stdout if unspecified).
+#
+# To make sure you're observing the side effects of checkpoint *before*
+# fast-import terminates (and thus writes out its state), check that the
+# fast-import process is still running using background_import_still_running
+# *after* evaluating the test conditions.
+background_import_then_checkpoint () {
+ options=$1
+ input_file=$2
+
+ mkfifo V.input
+ exec 8<>V.input
+ rm V.input
+
+ mkfifo V.output
+ exec 9<>V.output
+ rm V.output
+
+ git fast-import $options <&8 >&9 &
+ echo $! >V.pid
+ # We don't mind if fast-import has already died by the time the test
+ # ends.
+ test_when_finished "exec 8>&-; exec 9>&-; kill $(cat V.pid) || true"
+
+ # Start in the background to ensure we adhere strictly to (blocking)
+ # pipes writing sequence. We want to assume that the write below could
+ # block, e.g. if fast-import blocks writing its own output to &9
+ # because there is no reader on &9 yet.
+ (
+ cat "$input_file"
+ echo "checkpoint"
+ echo "progress checkpoint"
+ ) >&8 &
+
+ error=1 ;# assume the worst
+ while read output <&9
+ do
+ if test "$output" = "progress checkpoint"
+ then
+ error=0
+ break
+ fi
+ # otherwise ignore cruft
+ echo >&2 "cruft: $output"
+ done
+
+ if test $error -eq 1
+ then
+ false
+ fi
+}
+
+background_import_still_running () {
+ if ! kill -0 "$(cat V.pid)"
+ then
+ echo >&2 "background fast-import terminated too early"
+ false
+ fi
+}
+
+test_expect_success PIPE 'V: checkpoint helper does not get stuck with extra output' '
+ cat >input <<-INPUT_END &&
+ progress foo
+ progress bar
+
+ INPUT_END
+
+ background_import_then_checkpoint "" input &&
+ background_import_still_running
+'
+
+test_expect_success PIPE 'V: checkpoint updates refs after reset' '
+ cat >input <<-\INPUT_END &&
+ reset refs/heads/V
+ from refs/heads/U
+
+ INPUT_END
+
+ background_import_then_checkpoint "" input &&
+ test "$(git rev-parse --verify V)" = "$(git rev-parse --verify U)" &&
+ background_import_still_running
+'
+
+test_expect_success PIPE 'V: checkpoint updates refs and marks after commit' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/V
+ mark :1
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data 0
+ from refs/heads/U
+
+ INPUT_END
+
+ background_import_then_checkpoint "--export-marks=marks.actual" input &&
+
+ echo ":1 $(git rev-parse --verify V)" >marks.expected &&
+
+ test "$(git rev-parse --verify V^)" = "$(git rev-parse --verify U)" &&
+ test_cmp marks.expected marks.actual &&
+ background_import_still_running
+'
+
+# Re-create the exact same commit, but on a different branch: no new object is
+# created in the database, but the refs and marks still need to be updated.
+test_expect_success PIPE 'V: checkpoint updates refs and marks after commit (no new objects)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/V2
+ mark :2
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data 0
+ from refs/heads/U
+
+ INPUT_END
+
+ background_import_then_checkpoint "--export-marks=marks.actual" input &&
+
+ echo ":2 $(git rev-parse --verify V2)" >marks.expected &&
+
+ test "$(git rev-parse --verify V2)" = "$(git rev-parse --verify V)" &&
+ test_cmp marks.expected marks.actual &&
+ background_import_still_running
+'
+
+test_expect_success PIPE 'V: checkpoint updates tags after tag' '
+ cat >input <<-INPUT_END &&
+ tag Vtag
+ from refs/heads/V
+ tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data 0
+
+ INPUT_END
+
+ background_import_then_checkpoint "" input &&
+ git show-ref -d Vtag &&
+ background_import_still_running
+'
+
test_done
mkdir new &&
git --git-dir=new/.git init &&
git fast-export -C -C --signed-tags=strip --all > output &&
- grep "^C file6 file7\$" output &&
+ grep "^C file2 file4\$" output &&
cat output |
(cd new &&
git fast-import &&
test_cmp expected actual
'
+test_expect_success 'when using -C, do not declare copy when source of copy is also modified' '
+ test_create_repo src &&
+ echo a_line >src/file.txt &&
+ git -C src add file.txt &&
+ git -C src commit -m 1st_commit &&
+
+ cp src/file.txt src/file2.txt &&
+ echo another_line >>src/file.txt &&
+ git -C src add file.txt file2.txt &&
+ git -C src commit -m 2nd_commit &&
+
+ test_create_repo dst &&
+ git -C src fast-export --all -C | git -C dst fast-import &&
+ git -C src show >expected &&
+ git -C dst show >actual &&
+ test_cmp expected actual
+'
+
test_done
my $ok = join("|", qw(
TRACE
DEBUG
- USE_LOOKUP
TEST
.*_TEST
PROVE
if ($#ARGV < 1) {
die "usage: test-terminal program args";
}
+$ENV{TERM} = 'vt100';
my $master_in = new IO::Pty;
my $master_out = new IO::Pty;
my $master_err = new IO::Pty;
bufptr = nl + 1;
if (!strcmp(type, blob_type)) {
- item->tagged = &lookup_blob(&oid)->object;
+ item->tagged = (struct object *)lookup_blob(&oid);
} else if (!strcmp(type, tree_type)) {
- item->tagged = &lookup_tree(&oid)->object;
+ item->tagged = (struct object *)lookup_tree(&oid);
} else if (!strcmp(type, commit_type)) {
- item->tagged = &lookup_commit(&oid)->object;
+ item->tagged = (struct object *)lookup_commit(&oid);
} else if (!strcmp(type, tag_type)) {
- item->tagged = &lookup_tag(&oid)->object;
+ item->tagged = (struct object *)lookup_tag(&oid);
} else {
error("Unknown type %s", type);
item->tagged = NULL;
{
if (debug)
fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
- if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
- != buffer->len)
+ if (write_in_full(helper->helper->in, buffer->buf, buffer->len) < 0)
die_errno("Full write to remote helper failed");
}
{
if (debug)
fprintf(stderr, "Debug: Remote helper: -> %s", str);
- if (write_in_full(fd, str, strlen(str)) != strlen(str))
+ if (write_in_full(fd, str, strlen(str)) < 0)
die_errno("Full write to remote helper failed");
}
__attribute__((format (printf, 1, 2)))
static void transfer_debug(const char *fmt, ...)
{
+ /*
+ * NEEDSWORK: This function is sometimes used from multiple threads, and
+ * we end up using debug_enabled racily. That "should not matter" since
+ * we always write the same value, but it's still wrong. This function
+ * is listed in .tsan-suppressions for the time being.
+ */
+
va_list args;
char msgbuf[PBUFFERSIZE];
static int debug_enabled = -1;
int retval = MISSING_OBJECT;
struct dir_state *parents = NULL;
size_t parents_alloc = 0;
- ssize_t parents_nr = 0;
+ size_t i, parents_nr = 0;
unsigned char current_tree_sha1[20];
struct strbuf namebuf = STRBUF_INIT;
struct tree_desc t;
int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
- int i;
init_tree_desc(&t, NULL, 0UL);
strbuf_addstr(&namebuf, name);
"|//|\\*\\*|::|[/<>=]="),
IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
"[^ \t-]+"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
"[^<>= \t]+"),
PATTERNS("java",
"!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
"HEAD",
- RESOLVE_REF_READING,
+ 0,
wt->head_sha1, &flags);
if (!target)
return;
void write_file_buf(const char *path, const char *buf, size_t len)
{
int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (write_in_full(fd, buf, len) != len)
+ if (write_in_full(fd, buf, len) < 0)
die_errno(_("could not write to %s"), path);
if (close(fd))
die_errno(_("could not close %s"), path);