--- /dev/null
+Git v1.7.9.1 Release Notes
+==========================
+
+Fixes since v1.7.9
+------------------
+
+ * The makefile allowed environment variable X seep into it result in
+ command names suffixed with unnecessary strings.
+
+ * The set of included header files in compat/inet-{ntop,pton}
+ wrappers was updated for Windows some time ago, but in a way that
+ broke Solaris build.
+
+ * rpmbuild noticed an unpackaged but installed *.mo file and failed.
+
+ * Subprocesses spawned from various git programs were often left running
+ to completion even when the top-level process was killed.
+
+ * "git add -e" learned not to show a diff for an otherwise unmodified
+ submodule that only has uncommitted local changes in the patch
+ prepared by for the user to edit.
+
+ * Typo in "git branch --edit-description my-tpoic" was not diagnosed.
+
+ * Using "git grep -l/-L" together with options -W or --break may not
+ make much sense as the output is to only count the number of hits
+ and there is no place for file breaks, but the latter options made
+ "-l/-L" to miscount the hits.
+
+ * "git log --first-parent $pathspec" did not stay on the first parent
+ chain and veered into side branch from which the whole change to the
+ specified paths came.
+
+ * "git merge --no-edit $tag" failed to honor the --no-edit option.
+
+ * "git merge --ff-only $tag" failed because it cannot record the
+ required mergetag without creating a merge, but this is so common
+ operation for branch that is used _only_ to follow the upstream, so
+ it was changed to allow fast-forwarding without recording the mergetag.
+
+ * "git mergetool" now gives an empty file as the common base version
+ to the backend when dealing with the "both sides added, differently"
+ case.
+
+ * "git push -q" was not sufficiently quiet.
+
+ * When "git push" fails to update any refs, the client side did not
+ report an error correctly to the end user.
+
+ * "rebase" and "commit --amend" failed to work on commits with ancient
+ timestamps near year 1970.
+
+ * When asking for a tag to be pulled, "request-pull" did not show the
+ name of the tag prefixed with "tags/", which would have helped older
+ clients.
+
+ * "git submodule add $path" forgot to recompute the name to be stored
+ in .gitmodules when the submodule at $path was once added to the
+ superproject and already initialized.
+
+ * Many small corner case bugs on "git tag -n" was corrected.
+
+Also contains minor fixes and documentation updates.
--- /dev/null
+Git v1.7.9.2 Release Notes
+==========================
+
+Fixes since v1.7.9.1
+--------------------
+
+* The error message emitted when we see an empty loose object was
+ not phrased correctly.
+
+* The code to ask for password did not fall back to the terminal
+ input when GIT_ASKPASS is set but does not work (e.g. lack of X
+ with GUI askpass helper).
+
+* map_user() was not rewriting its output correctly, which resulted
+ in the user visible symptom that "git blame -e" sometimes showed
+ excess '>' at the end of email addresses.
+
+* "git checkout -b" did not allow switching out of an unborn branch.
+
+* "git commit" refused to create a commit when entries added with
+ "add -N" remained in the index, without telling Git what their content
+ in the next commit should be. We should have created the commit without
+ these paths.
+
+* Search box in "gitweb" did not accept non-ASCII characters correctly.
+
+* Search interface of "gitweb" did not show multiple matches in the same file
+ correctly.
+
+Also contains minor fixes and documentation updates.
SYNOPSIS
--------
[verse]
-'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
+'git' [--version] [--help] [-c <name>=<value>]
+ [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
- [-c <name>=<value>]
- [--help] <command> [<args>]
+ <command> [<args>]
DESCRIPTION
-----------
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.9/git.html[documentation for release 1.7.9]
+* link:v1.7.9.1/git.html[documentation for release 1.7.9.1]
* release notes for
+ link:RelNotes/1.7.9.1.txt[1.7.9.1],
link:RelNotes/1.7.9.txt[1.7.9].
* link:v1.7.8.4/git.html[documentation for release 1.7.8.4]
link:RelNotes/1.7.7.1.txt[1.7.7.1],
link:RelNotes/1.7.7.txt[1.7.7].
-* link:v1.7.6.5/git.html[documentation for release 1.7.6.5]
+* link:v1.7.6.6/git.html[documentation for release 1.7.6.6]
* release notes for
+ link:RelNotes/1.7.6.6.txt[1.7.6.6],
link:RelNotes/1.7.6.5.txt[1.7.6.5],
link:RelNotes/1.7.6.4.txt[1.7.6.4],
link:RelNotes/1.7.6.3.txt[1.7.6.3],
are available in the git repository at:
- example.com:/git/froboz.git frotz-for-xyzzy
+ example.com:/git/froboz.git tags/frotz-for-xyzzy
for you to fetch changes up to 703f05ad5835c...:
integrates the tag named in the request, with:
------------
- $ git pull example.com:/git/froboz.git/ frotz-for-xyzzy
+ $ git pull example.com:/git/froboz.git/ tags/frotz-for-xyzzy
------------
This operation will always open an editor to allow the integrator to fine
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.9
+DEF_VER=v1.7.9.1
LF='
'
If you're willing to trade off (much) longer build time for a later
faster git you can also do a profile feedback build with
- $ make profile-all
- # make prefix=... install
+ $ make prefix=/usr PROFILE=BUILD all
+ # make prefix=/usr PROFILE=BUILD install
This will run the complete test suite as training workload and then
rebuild git with the generated profile feedback. This results in a git
which is a few percent faster on CPU intensive workloads. This
may be a good tradeoff for distribution packagers.
-Note that the profile feedback build stage currently generates
-a lot of additional compiler warnings.
+Or if you just want to install a profile-optimized version of git into
+your home directory, you could run:
+
+ $ make PROFILE=BUILD install
+
+As a caveat: a profile-optimized build takes a *lot* longer since the
+git tree must be built twice, and in order for the profiling
+measurements to work properly, ccache must be disabled and the test
+suite has to be run using only a single CPU. In addition, the profile
+feedback build stage currently generates a lot of additional compiler
+warnings.
Issues of note:
export prefix bindir sharedir sysconfdir gitwebdir localedir
-CC = gcc
+CC = cc
AR = ar
RM = rm -f
DIFF = diff
PROGRAM_OBJS += sh-i18n--envsubst.o
PROGRAM_OBJS += credential-store.o
+# Binary suffix, set to .exe for Windows builds
+X =
+
PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
TEST_PROGRAMS_NEED_X += test-chmtime
export ASCIIDOC7
endif
+### profile feedback build
+#
+
+# Can adjust this to be a global directory if you want to do extended
+# data gathering
+PROFILE_DIR := $(CURDIR)
+
+ifeq ("$(PROFILE)","GEN")
+ CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
+ EXTLIBS += -lgcov
+ export CCACHE_DISABLE=t
+ V=1
+else
+ifneq ("$(PROFILE)","")
+ CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
+ export CCACHE_DISABLE=t
+ V=1
+endif
+endif
+
# Shell quote (do not use $(call) to accommodate ancient setups);
SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
SHELL = $(SHELL_PATH)
-all:: shell_compatibility_test $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
+all:: shell_compatibility_test
+
+ifeq "$(PROFILE)" "BUILD"
+ifeq ($(filter all,$(MAKECMDGOALS)),all)
+all:: profile-clean
+ $(MAKE) PROFILE=GEN all
+ $(MAKE) PROFILE=GEN -j1 test
+endif
+endif
+
+all:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
ifneq (,$X)
$(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
endif
$(RM) configure
$(RM) po/git.pot
-clean:
+profile-clean:
+ $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
+ $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
+
+clean: profile-clean
$(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
endif
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
-.PHONY: all install clean strip
+.PHONY: all install profile-clean clean strip
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
.PHONY: FORCE cscope
cover_db_html: cover_db
cover -report html -outputdir cover_db_html cover_db
-### profile feedback build
-#
-.PHONY: profile-all profile-clean
-
-PROFILE_GEN_CFLAGS := $(CFLAGS) -fprofile-generate -DNO_NORETURN=1
-PROFILE_USE_CFLAGS := $(CFLAGS) -fprofile-use -fprofile-correction -DNO_NORETURN=1
-
-profile-clean:
- $(RM) $(addsuffix *.gcda,$(object_dirs))
- $(RM) $(addsuffix *.gcno,$(object_dirs))
-
-profile-all: profile-clean
- $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" all
- $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" -j1 test
- $(MAKE) CFLAGS="$(PROFILE_USE_CFLAGS)" all
-Documentation/RelNotes/1.7.9.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.9.2.txt
\ No newline at end of file
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
out = open(file, O_CREAT | O_WRONLY, 0644);
if (out < 0)
die (_("Could not open '%s' for writing."), file);
with_commit, argv);
else if (edit_description) {
const char *branch_name;
+ struct strbuf branch_ref = STRBUF_INIT;
+
if (detached)
die("Cannot give description to detached HEAD");
if (!argc)
branch_name = argv[0];
else
usage_with_options(builtin_branch_usage, options);
+
+ strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
+ if (!ref_exists(branch_ref.buf)) {
+ strbuf_release(&branch_ref);
+
+ if (!argc)
+ return error("No commit on branch '%s' yet.",
+ branch_name);
+ else
+ return error("No such branch '%s'.", branch_name);
+ }
+ strbuf_release(&branch_ref);
+
if (edit_branch_description(branch_name))
return 1;
} else if (rename) {
return argcount;
}
+static int switch_unborn_to_new_branch(struct checkout_opts *opts)
+{
+ int status;
+ struct strbuf branch_ref = STRBUF_INIT;
+
+ strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
+ status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+ strbuf_release(&branch_ref);
+ return status;
+}
+
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
if (opts.writeout_stage)
die(_("--ours/--theirs is incompatible with switching branches."));
+ if (!new.commit) {
+ unsigned char rev[20];
+ int flag;
+
+ if (!read_ref_full("HEAD", rev, 0, &flag) &&
+ (flag & REF_ISSYMREF) && is_null_sha1(rev))
+ return switch_unborn_to_new_branch(&opts);
+ }
return switch_branches(&opts, &new);
}
static const char *whence_s(void)
{
- char *s = "";
+ const char *s = "";
switch (whence) {
case FROM_COMMIT:
break;
case FROM_MERGE:
- s = "merge";
+ s = _("merge");
break;
case FROM_CHERRY_PICK:
- s = "cherry-pick";
+ s = _("cherry-pick");
break;
}
if (author_message) {
const char *a, *lb, *rb, *eol;
+ size_t len;
a = strstr(author_message_buffer, "\nauthor ");
if (!a)
(a + strlen("\nauthor "))));
email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
+ len = eol - (rb + strlen("> "));
+ date = xmalloc(len + 2);
+ *date = '@';
+ memcpy(date + 1, rb + strlen("> "), len);
+ date[len + 1] = '\0';
}
if (force_author) {
#ifndef NO_PTHREADS
if (use_threads) {
- if (opt.pre_context || opt.post_context || opt.file_break ||
- opt.funcbody)
+ if (!(opt.name_only || opt.unmatch_name_only || opt.count)
+ && (opt.pre_context || opt.post_context ||
+ opt.file_break || opt.funcbody))
skip_first_line = 1;
start_threads(&opt);
}
static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit;
+static int fast_forward_only, option_edit = -1;
static int allow_trivial = 1, have_message;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
"create a single commit instead of doing a merge"),
OPT_BOOLEAN(0, "commit", &option_commit,
"perform a commit if the merge succeeds (default)"),
- OPT_BOOLEAN('e', "edit", &option_edit,
+ OPT_BOOL('e', "edit", &option_edit,
"edit message before committing"),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
"allow fast-forward (default)"),
sha1_to_hex(commit->object.sha1));
setenv(buf.buf, argv[i], 1);
strbuf_reset(&buf);
- if (merge_remote_util(commit) &&
+ if (!fast_forward_only &&
+ merge_remote_util(commit) &&
merge_remote_util(commit)->obj &&
merge_remote_util(commit)->obj->type == OBJ_TAG) {
- option_edit = 1;
+ if (option_edit < 0)
+ option_edit = 1;
allow_fast_forward = 0;
}
}
+ if (option_edit < 0)
+ option_edit = 0;
+
if (!use_strategies) {
if (!remoteheads->next)
add_strategies(pull_twohead, DEFAULT_TWOHEAD);
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
+static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
else
packet_write(1, "%s %s%c%s%s\n",
sha1_to_hex(sha1), path, 0,
- " report-status delete-refs side-band-64k",
+ " report-status delete-refs side-band-64k quiet",
prefer_ofs_delta ? " ofs-delta" : "");
sent_capabilities = 1;
}
refname = line + 82;
reflen = strlen(refname);
if (reflen + 82 < len) {
- if (strstr(refname + reflen + 1, "report-status"))
+ const char *feature_list = refname + reflen + 1;
+ if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
- if (strstr(refname + reflen + 1, "side-band-64k"))
+ if (parse_feature_request(feature_list, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
+ if (parse_feature_request(feature_list, "quiet"))
+ quiet = 1;
}
cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
if (ntohl(hdr.hdr_entries) < unpack_limit) {
int code, i = 0;
- const char *unpacker[4];
+ const char *unpacker[5];
unpacker[i++] = "unpack-objects";
+ if (quiet)
+ unpacker[i++] = "-q";
if (fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
const char *arg = *argv++;
if (*arg == '-') {
+ if (!strcmp(arg, "--quiet")) {
+ quiet = 1;
+ continue;
+ }
+
if (!strcmp(arg, "--advertise-refs")) {
advertise_refs = 1;
continue;
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
use_sideband = 1;
+ if (!server_supports("quiet"))
+ args->quiet = 0;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
- if (!cmds_sent && (status_report || use_sideband)) {
- packet_buf_write(&req_buf, "%s %s %s%c%s%s",
+ if (!cmds_sent && (status_report || use_sideband || args->quiet)) {
+ packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
old_hex, new_hex, ref->name, 0,
status_report ? " report-status" : "",
- use_sideband ? " side-band-64k" : "");
+ use_sideband ? " side-band-64k" : "",
+ args->quiet ? " quiet" : "");
}
else
packet_buf_write(&req_buf, "%s %s %s",
args.force_update = 1;
continue;
}
+ if (!strcmp(arg, "--quiet")) {
+ args.quiet = 1;
+ continue;
+ }
if (!strcmp(arg, "--verbose")) {
args.verbose = 1;
continue;
return contains_recurse(candidate, want);
}
+static void show_tag_lines(const unsigned char *sha1, int lines)
+{
+ int i;
+ unsigned long size;
+ enum object_type type;
+ char *buf, *sp, *eol;
+ size_t len;
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ die_errno("unable to read object %s", sha1_to_hex(sha1));
+ if (type != OBJ_COMMIT && type != OBJ_TAG)
+ goto free_return;
+ if (!size)
+ die("an empty %s object %s?",
+ typename(type), sha1_to_hex(sha1));
+
+ /* skip header */
+ sp = strstr(buf, "\n\n");
+ if (!sp)
+ goto free_return;
+
+ /* only take up to "lines" lines, and strip the signature from a tag */
+ if (type == OBJ_TAG)
+ size = parse_signature(buf, size);
+ for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
+ if (i)
+ printf("\n ");
+ eol = memchr(sp, '\n', size - (sp - buf));
+ len = eol ? eol - sp : size - (sp - buf);
+ fwrite(sp, len, 1, stdout);
+ if (!eol)
+ break;
+ sp = eol + 1;
+ }
+free_return:
+ free(buf);
+}
+
static int show_reference(const char *refname, const unsigned char *sha1,
int flag, void *cb_data)
{
struct tag_filter *filter = cb_data;
if (match_pattern(filter->patterns, refname)) {
- int i;
- unsigned long size;
- enum object_type type;
- char *buf, *sp, *eol;
- size_t len;
-
if (filter->with_commit) {
struct commit *commit;
return 0;
}
printf("%-15s ", refname);
-
- buf = read_sha1_file(sha1, &type, &size);
- if (!buf || !size)
- return 0;
-
- /* skip header */
- sp = strstr(buf, "\n\n");
- if (!sp) {
- free(buf);
- return 0;
- }
- /* only take up to "lines" lines, and strip the signature */
- size = parse_signature(buf, size);
- for (i = 0, sp += 2;
- i < filter->lines && sp < buf + size;
- i++) {
- if (i)
- printf("\n ");
- eol = memchr(sp, '\n', size - (sp - buf));
- len = eol ? eol - sp : size - (sp - buf);
- fwrite(sp, len, 1, stdout);
- if (!eol)
- break;
- sp = eol + 1;
- }
+ show_tag_lines(sha1, filter->lines);
putchar('\n');
- free(buf);
}
return 0;
funny = 0;
for (i = 0; i < entries; i++) {
struct cache_entry *ce = cache[i];
- if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
+ if (ce_stage(ce)) {
if (silent)
return -1;
if (10 < ++funny) {
mode, sha1_to_hex(sha1), entlen+baselen, path);
}
- if (ce->ce_flags & CE_REMOVE)
- continue; /* entry being removed */
+ if (ce->ce_flags & (CE_REMOVE | CE_INTENT_TO_ADD))
+ continue; /* entry being removed or placeholder */
strbuf_grow(&buffer, entlen + 100);
strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
};
extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
extern int server_supports(const char *feature);
+extern const char *parse_feature_request(const char *features, const char *feature);
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
* SOFTWARE.
*/
-#include <errno.h>
-#include <sys/types.h>
-
#include "../git-compat-util.h"
-#include <stdio.h>
-#include <string.h>
-
#ifndef NS_INADDRSZ
#define NS_INADDRSZ 4
#endif
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <errno.h>
-#include <sys/types.h>
-
#include "../git-compat-util.h"
-#include <stdio.h>
-#include <string.h>
-
#ifndef NS_INT16SZ
#define NS_INT16SZ 2
#endif
int server_supports(const char *feature)
{
- return server_capabilities &&
- strstr(server_capabilities, feature) != NULL;
+ return !!parse_feature_request(server_capabilities, feature);
+}
+
+const char *parse_feature_request(const char *feature_list, const char *feature)
+{
+ int len;
+
+ if (!feature_list)
+ return NULL;
+
+ len = strlen(feature);
+ while (*feature_list) {
+ const char *found = strstr(feature_list, feature);
+ if (!found)
+ return NULL;
+ if ((feature_list == found || isspace(found[-1])) &&
+ (!found[len] || isspace(found[len]) || found[len] == '='))
+ return found;
+ feature_list = found + 1;
+ }
+ return NULL;
}
enum protocol {
fi
local f="$w$i$s$u"
- printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
+ printf -- "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
fi
}
# 4: A suffix to be appended to each possible completion word (optional).
__gitcomp ()
{
- local cur_="$cur"
+ local cur_="${3-$cur}"
- if [ $# -gt 2 ]; then
- cur_="$3"
- fi
case "$cur_" in
--*=)
COMPREPLY=()
# appended.
__gitcomp_nl ()
{
- local s=$'\n' IFS=' '$'\t'$'\n'
- local cur_="$cur" suffix=" "
-
- if [ $# -gt 2 ]; then
- cur_="$3"
- if [ $# -gt 3 ]; then
- suffix="$4"
- fi
- fi
-
- IFS=$s
- COMPREPLY=($(compgen -P "${2-}" -S "$suffix" -W "$1" -- "$cur_"))
+ local IFS=$'\n'
+ COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
}
__git_heads ()
__git_remotes ()
{
- local i ngoff IFS=$'\n' d="$(__gitdir)"
- __git_shopt -q nullglob || ngoff=1
- __git_shopt -s nullglob
- for i in "$d/remotes"/*; do
- echo ${i#$d/remotes/}
- done
- [ "$ngoff" ] && __git_shopt -u nullglob
+ local i IFS=$'\n' d="$(__gitdir)"
+ test -d "$d/remotes" && ls -1 "$d/remotes"
for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
i="${i#remote.}"
echo "${i/.url*/}"
# is needed.
__git_compute_merge_strategies ()
{
- : ${__git_merge_strategies:=$(__git_list_merge_strategies)}
+ test -n "$__git_merge_strategies" ||
+ __git_merge_strategies=$(__git_list_merge_strategies)
}
__git_complete_revlist_file ()
__git_all_commands=
__git_compute_all_commands ()
{
- : ${__git_all_commands:=$(__git_list_all_commands)}
+ test -n "$__git_all_commands" ||
+ __git_all_commands=$(__git_list_all_commands)
}
__git_list_porcelain_commands ()
__git_compute_porcelain_commands ()
{
__git_compute_all_commands
- : ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
+ test -n "$__git_porcelain_commands" ||
+ __git_porcelain_commands=$(__git_list_porcelain_commands)
}
__git_pretty_aliases ()
__gitcomp "
--color --no-color --verbose --abbrev= --no-abbrev
--track --no-track --contains --merged --no-merged
- --set-upstream
+ --set-upstream --edit-description --list
"
;;
*)
__git_merge_options="
--no-commit --no-stat --log --no-log --squash --strategy
- --commit --stat --no-squash --ff --no-ff --ff-only
+ --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
"
_git_merge ()
complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
|| complete -o default -o nospace -F _git git.exe
fi
-
-if [[ -n ${ZSH_VERSION-} ]]; then
- __git_shopt () {
- local option
- if [ $# -ne 2 ]; then
- echo "USAGE: $0 (-q|-s|-u) <option>" >&2
- return 1
- fi
- case "$2" in
- nullglob)
- option="$2"
- ;;
- *)
- echo "$0: invalid option: $2" >&2
- return 1
- esac
- case "$1" in
- -q) setopt | grep -q "$option" ;;
- -u) unsetopt "$option" ;;
- -s) setopt "$option" ;;
- *)
- echo "$0: invalid flag: $1" >&2
- return 1
- esac
- }
-else
- __git_shopt () {
- shopt "$@"
- }
-fi
*
* No surprises, and works with signed and unsigned chars.
*/
-#include "cache.h"
+#include "git-compat-util.h"
enum {
S = GIT_SPACE,
return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
}
+/*
+ * Parse a string like "0 +0000" as ancient timestamp near epoch, but
+ * only when it appears not as part of any other string.
+ */
+static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset)
+{
+ char *end;
+ unsigned long stamp;
+ int ofs;
+
+ if (*date < '0' || '9' <= *date)
+ return -1;
+ stamp = strtoul(date, &end, 10);
+ if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
+ return -1;
+ date = end + 2;
+ ofs = strtol(date, &end, 10);
+ if ((*end != '\0' && (*end != '\n')) || end != date + 4)
+ return -1;
+ ofs = (ofs / 100) * 60 + (ofs % 100);
+ if (date[-1] == '-')
+ ofs = -ofs;
+ *timestamp = stamp;
+ *offset = ofs;
+ return 0;
+}
+
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
*offset = -1;
tm_gmt = 0;
+ if (*date == '@' &&
+ !match_object_header_date(date + 1, timestamp, offset))
+ return 0; /* success */
for (;;) {
int match = 0;
unsigned char c = *date;
#undef isdigit
#undef isalpha
#undef isalnum
+#undef islower
+#undef isupper
#undef tolower
#undef toupper
extern unsigned char sane_ctype[256];
#define isdigit(x) sane_istest(x,GIT_DIGIT)
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define islower(x) sane_iscase(x, 1)
+#define isupper(x) sane_iscase(x, 0)
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
return x;
}
+static inline int sane_iscase(int x, int is_lower)
+{
+ if (!sane_istest(x, GIT_ALPHA))
+ return 0;
+
+ if (is_lower)
+ return (x & 0x20) != 0;
+ else
+ return (x & 0x20) == 0;
+}
+
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
{
unsigned long ul;
}
checkout_staged_file () {
- tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ')
+ tmpfile=$(expr \
+ "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
+ : '\([^ ]*\) ')
if test $? -eq 0 -a -n "$tmpfile" ; then
mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
+ else
+ >"$3"
fi
}
mv -- "$MERGED" "$BACKUP"
cp -- "$BACKUP" "$MERGED"
- base_present && checkout_staged_file 1 "$MERGED" "$BASE"
- local_present && checkout_staged_file 2 "$MERGED" "$LOCAL"
- remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
+ checkout_staged_file 1 "$MERGED" "$BASE"
+ checkout_staged_file 2 "$MERGED" "$LOCAL"
+ checkout_staged_file 3 "$MERGED" "$REMOTE"
if test -z "$local_mode" -o -z "$remote_mode"; then
echo "Deleted merge conflict for '$MERGED':"
find_matching_ref='
sub abbr {
my $ref = shift;
- if ($ref =~ s|refs/heads/|| || $ref =~ s|refs/tags/||) {
+ if ($ref =~ s|^refs/heads/|| || $ref =~ s|^refs/tags/|tags/|) {
return $ref;
} else {
return $ref;
s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
g
- s/^author [^<]* <[^>]*> \(.*\)$/\1/
+ s/^author [^<]* <[^>]*> \(.*\)$/@\1/
s/.*/GIT_AUTHOR_DATE='\''&'\''/p
q
gitdir=
gitdir_base=
name=$(module_name "$path" 2>/dev/null)
+ test -n "$name" || name="$path"
base_path=$(dirname "$path")
gitdir=$(git rev-parse --git-dir)
* if we fail because the command is not found, it is
* OK to return. Otherwise, we just pass along the status code.
*/
- status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+ status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
if (status >= 0 || errno != ENOENT)
exit(status);
%else
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
+rm -rf $RPM_BUILD_ROOT%{_datadir}/locale
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
install -m 644 -T contrib/completion/git-completion.bash $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/git
# as base URL.
# Therefore, if we needed to strip PATH_INFO, then we know that we have
# to build the base URL ourselves:
- our $path_info = $ENV{"PATH_INFO"};
+ our $path_info = decode_utf8($ENV{"PATH_INFO"});
if ($path_info) {
if ($my_url =~ s,\Q$path_info\E$,, &&
$my_uri =~ s,\Q$path_info\E$,, &&
while (my ($name, $symbol) = each %cgi_param_mapping) {
if ($symbol eq 'opt') {
- $input_params{$name} = [ $cgi->param($symbol) ];
+ $input_params{$name} = [ map { decode_utf8($_) } $cgi->param($symbol) ];
} else {
- $input_params{$name} = $cgi->param($symbol);
+ $input_params{$name} = decode_utf8($cgi->param($symbol));
}
}
}
}
my $cloud;
- my $matched = $cgi->param('by_tag');
+ my $matched = $input_params{'ctag'};
if (eval { require HTML::TagCloud; 1; }) {
$cloud = HTML::TagCloud->new;
foreach my $ctag (sort keys %ctags_lc) {
-values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
$cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
" search:\n",
- $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+ $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
"<span title=\"Extended regular expression\">" .
$cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
-checked => $search_use_regexp) .
my $check_forks = gitweb_check_feature('forks');
my $show_ctags = gitweb_check_feature('ctags');
- my $tagfilter = $show_ctags ? $cgi->param('by_tag') : undef;
+ my $tagfilter = $show_ctags ? $input_params{'ctag'} : undef;
$check_forks = undef
if ($tagfilter || $searchtext);
my $alternate = 1;
my $matches = 0;
my $lastfile = '';
+ my $file_href;
while (my $line = <$fd>) {
chomp $line;
- my ($file, $file_href, $lno, $ltext, $binary);
+ my ($file, $lno, $ltext, $binary);
last if ($matches++ > 1000);
if ($line =~ /^Binary file (.+) matches$/) {
$file = $1;
}
print $cgi->startform(-method => "get") .
"<p class=\"projsearch\">Search:\n" .
- $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+ $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
"</p>" .
$cgi->end_form() . "\n";
git_project_list_body(\@list, $order);
sub git_blame_common {
my $format = shift || 'porcelain';
- if ($format eq 'porcelain' && $cgi->param('js')) {
+ if ($format eq 'porcelain' && $input_params{'javascript'}) {
$format = 'incremental';
$action = 'blame_incremental'; # for page title etc
}
unsigned trash_remote_new:1, trash_only_new:1;
};
-struct string_list {
- struct string_list *next;
- char string[1];
-};
-
-struct channel_conf {
- struct channel_conf *next;
- char *name;
- struct store_conf *master, *slave;
- char *master_name, *slave_name;
- char *sync_state;
- struct string_list *patterns;
- int mops, sops;
- unsigned max_messages; /* for slave only */
-};
-
-struct group_conf {
- struct group_conf *next;
- char *name;
- struct string_list *channels;
-};
-
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
struct message {
struct message *next;
- /* struct string_list *keywords; */
size_t size; /* zero implies "not fetched" */
int uid;
unsigned char flags, status;
int map_user(struct string_list *map,
char *email, int maxlen_email, char *name, int maxlen_name)
{
- char *p;
+ char *end_of_email;
struct string_list_item *item;
struct mailmap_entry *me;
char buf[1024], *mailbuf;
int i;
/* figure out space requirement for email */
- p = strchr(email, '>');
- if (!p) {
+ end_of_email = strchr(email, '>');
+ if (!end_of_email) {
/* email passed in might not be wrapped in <>, but end with a \0 */
- p = memchr(email, '\0', maxlen_email);
- if (!p)
+ end_of_email = memchr(email, '\0', maxlen_email);
+ if (!end_of_email)
return 0;
}
- if (p - email + 1 < sizeof(buf))
+ if (end_of_email - email + 1 < sizeof(buf))
mailbuf = buf;
else
- mailbuf = xmalloc(p - email + 1);
+ mailbuf = xmalloc(end_of_email - email + 1);
/* downcase the email address */
- for (i = 0; i < p - email; i++)
+ for (i = 0; i < end_of_email - email; i++)
mailbuf[i] = tolower(email[i]);
mailbuf[i] = 0;
}
if (maxlen_email && mi->email)
strlcpy(email, mi->email, maxlen_email);
+ else
+ *end_of_email = '\0';
if (maxlen_name && mi->name)
strlcpy(name, mi->name, maxlen_name);
debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : "");
struct child_process pass;
const char *args[3];
static struct strbuf buffer = STRBUF_INIT;
+ int err = 0;
args[0] = cmd;
args[1] = prompt;
pass.out = -1;
if (start_command(&pass))
- exit(1);
+ return NULL;
- strbuf_reset(&buffer);
if (strbuf_read(&buffer, pass.out, 20) < 0)
- die("failed to get '%s' from %s\n", prompt, cmd);
+ err = 1;
close(pass.out);
if (finish_command(&pass))
- exit(1);
+ err = 1;
+
+ if (err) {
+ error("unable to read askpass response from '%s'", cmd);
+ strbuf_release(&buffer);
+ return NULL;
+ }
strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
- return buffer.buf;
+ return strbuf_detach(&buffer, NULL);
}
char *git_prompt(const char *prompt, int flags)
{
- char *r;
+ char *r = NULL;
if (flags & PROMPT_ASKPASS) {
const char *askpass;
if (!askpass)
askpass = getenv("SSH_ASKPASS");
if (askpass && *askpass)
- return do_askpass(askpass, prompt);
+ r = do_askpass(askpass, prompt);
}
- r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
if (!r)
- die_errno("could not read '%s'", prompt);
+ r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
+ if (!r) {
+ /* prompts already contain ": " at the end */
+ die("could not read %s%s", prompt, strerror(errno));
+ }
return r;
}
argv[argc++] = "--thin";
if (options.dry_run)
argv[argc++] = "--dry-run";
- if (options.verbosity > 1)
+ if (options.verbosity == 0)
+ argv[argc++] = "--quiet";
+ else if (options.verbosity > 1)
argv[argc++] = "--verbose";
argv[argc++] = url;
for (i = 0; i < nr_spec; i++)
static void parse_push(struct strbuf *buf)
{
char **specs = NULL;
- int alloc_spec = 0, nr_spec = 0, i;
+ int alloc_spec = 0, nr_spec = 0, i, ret;
do {
if (!prefixcmp(buf->buf, "push ")) {
break;
} while (1);
- if (push(nr_spec, specs))
- exit(128); /* error already reported */
-
+ ret = push(nr_spec, specs);
printf("\n");
fflush(stdout);
+ if (ret)
+ exit(128); /* error already reported */
+
free_specs:
for (i = 0; i < nr_spec; i++)
free(specs[i]);
base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0);
if (!num_theirs)
- strbuf_addf(sb, "Your branch is ahead of '%s' "
- "by %d commit%s.\n",
- base, num_ours, (num_ours == 1) ? "" : "s");
+ strbuf_addf(sb,
+ Q_("Your branch is ahead of '%s' by %d commit.\n",
+ "Your branch is ahead of '%s' by %d commits.\n",
+ num_ours),
+ base, num_ours);
else if (!num_ours)
- strbuf_addf(sb, "Your branch is behind '%s' "
- "by %d commit%s, "
- "and can be fast-forwarded.\n",
- base, num_theirs, (num_theirs == 1) ? "" : "s");
+ strbuf_addf(sb,
+ Q_("Your branch is behind '%s' by %d commit, "
+ "and can be fast-forwarded.\n",
+ "Your branch is behind '%s' by %d commits, "
+ "and can be fast-forwarded.\n",
+ num_theirs),
+ base, num_theirs);
else
- strbuf_addf(sb, "Your branch and '%s' have diverged,\n"
- "and have %d and %d different commit(s) each, "
- "respectively.\n",
- base, num_ours, num_theirs);
+ strbuf_addf(sb,
+ Q_("Your branch and '%s' have diverged,\n"
+ "and have %d and %d different commit each, "
+ "respectively.\n",
+ "Your branch and '%s' have diverged,\n"
+ "and have %d and %d different commits each, "
+ "respectively.\n",
+ num_theirs),
+ base, num_ours, num_theirs);
return 1;
}
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp, *parent;
- int tree_changed = 0, tree_same = 0;
+ int tree_changed = 0, tree_same = 0, nth_parent = 0;
/*
* If we don't do pruning, everything is interesting
while ((parent = *pp) != NULL) {
struct commit *p = parent->item;
+ /*
+ * Do not compare with later parents when we care only about
+ * the first parent chain, in order to avoid derailing the
+ * traversal to follow a side branch that brought everything
+ * in the path we are limited to by the pathspec.
+ */
+ if (revs->first_parent_only && nth_parent++)
+ break;
if (parse_commit(p) < 0)
die("cannot simplify commit %s (because of %s)",
sha1_to_hex(commit->object.sha1),
#include "cache.h"
#include "run-command.h"
#include "exec_cmd.h"
+#include "sigchain.h"
#include "argv-array.h"
+struct child_to_clean {
+ pid_t pid;
+ struct child_to_clean *next;
+};
+static struct child_to_clean *children_to_clean;
+static int installed_child_cleanup_handler;
+
+static void cleanup_children(int sig)
+{
+ while (children_to_clean) {
+ struct child_to_clean *p = children_to_clean;
+ children_to_clean = p->next;
+ kill(p->pid, sig);
+ free(p);
+ }
+}
+
+static void cleanup_children_on_signal(int sig)
+{
+ cleanup_children(sig);
+ sigchain_pop(sig);
+ raise(sig);
+}
+
+static void cleanup_children_on_exit(void)
+{
+ cleanup_children(SIGTERM);
+}
+
+static void mark_child_for_cleanup(pid_t pid)
+{
+ struct child_to_clean *p = xmalloc(sizeof(*p));
+ p->pid = pid;
+ p->next = children_to_clean;
+ children_to_clean = p;
+
+ if (!installed_child_cleanup_handler) {
+ atexit(cleanup_children_on_exit);
+ sigchain_push_common(cleanup_children_on_signal);
+ installed_child_cleanup_handler = 1;
+ }
+}
+
+static void clear_child_for_cleanup(pid_t pid)
+{
+ struct child_to_clean **last, *p;
+
+ last = &children_to_clean;
+ for (p = children_to_clean; p; p = p->next) {
+ if (p->pid == pid) {
+ *last = p->next;
+ free(p);
+ return;
+ }
+ }
+}
+
static inline void close_pair(int fd[2])
{
close(fd[0]);
} else {
error("waitpid is confused (%s)", argv0);
}
+
+ clear_child_for_cleanup(pid);
+
errno = failed_errno;
return code;
}
if (cmd->pid < 0)
error("cannot fork() for %s: %s", cmd->argv[0],
strerror(failed_errno = errno));
+ else if (cmd->clean_on_exit)
+ mark_child_for_cleanup(cmd->pid);
/*
* Wait for child's execvp. If the execvp succeeds (or if fork()
cmd->pid = -1;
}
close(notify_pipe[0]);
+
}
#else
{
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+ if (cmd->clean_on_exit && cmd->pid >= 0)
+ mark_child_for_cleanup(cmd->pid);
if (cmd->env)
free_environ(env);
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
+ cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
}
int run_command_v_opt(const char **argv, int opt)
exit(!!async->proc(proc_in, proc_out, async->data));
}
+ mark_child_for_cleanup(async->pid);
+
if (need_in)
close(fdin[0]);
else if (async->in)
unsigned silent_exec_failure:1;
unsigned stdout_to_stderr:1;
unsigned use_shell:1;
+ unsigned clean_on_exit:1;
void (*preexec_cb)(void);
};
#define RUN_COMMAND_STDOUT_TO_STDERR 4
#define RUN_SILENT_EXEC_FAILURE 8
#define RUN_USING_SHELL 16
+#define RUN_CLEAN_ON_EXIT 32
int run_command_v_opt(const char **argv, int opt);
/*
0
};
+static struct packed_git *last_found_pack;
+
static struct cached_object *find_cached_object(const unsigned char *sha1)
{
int i;
close_pack_index(p);
free(p->bad_object_sha1);
*pp = p->next;
+ if (last_found_pack == p)
+ last_found_pack = NULL;
free(p);
return;
}
if (!fstat(fd, &st)) {
*size = xsize_t(st.st_size);
+ if (!*size) {
+ /* mmap() is forbidden on empty files */
+ error("object file %s is empty", sha1_file_name(sha1));
+ return NULL;
+ }
map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
}
close(fd);
return !open_packed_git(p);
}
+static int fill_pack_entry(const unsigned char *sha1,
+ struct pack_entry *e,
+ struct packed_git *p)
+{
+ off_t offset;
+
+ if (p->num_bad_objects) {
+ unsigned i;
+ for (i = 0; i < p->num_bad_objects; i++)
+ if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+ return 0;
+ }
+
+ offset = find_pack_entry_one(sha1, p);
+ if (!offset)
+ return 0;
+
+ /*
+ * We are about to tell the caller where they can locate the
+ * requested object. We better make sure the packfile is
+ * still here and can be accessed before supplying that
+ * answer, as it may have been deleted since the index was
+ * loaded!
+ */
+ if (!is_pack_valid(p)) {
+ warning("packfile %s cannot be accessed", p->pack_name);
+ return 0;
+ }
+ e->offset = offset;
+ e->p = p;
+ hashcpy(e->sha1, sha1);
+ return 1;
+}
+
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
{
- static struct packed_git *last_found = (void *)1;
struct packed_git *p;
- off_t offset;
prepare_packed_git();
if (!packed_git)
return 0;
- p = (last_found == (void *)1) ? packed_git : last_found;
- do {
- if (p->num_bad_objects) {
- unsigned i;
- for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
- goto next;
- }
+ if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
+ return 1;
- offset = find_pack_entry_one(sha1, p);
- if (offset) {
- /*
- * We are about to tell the caller where they can
- * locate the requested object. We better make
- * sure the packfile is still here and can be
- * accessed before supplying that answer, as
- * it may have been deleted since the index
- * was loaded!
- */
- if (!is_pack_valid(p)) {
- warning("packfile %s cannot be accessed", p->pack_name);
- goto next;
- }
- e->offset = offset;
- e->p = p;
- hashcpy(e->sha1, sha1);
- last_found = p;
- return 1;
- }
+ for (p = packed_git; p; p = p->next) {
+ if (p == last_found_pack || !fill_pack_entry(sha1, e, p))
+ continue;
- next:
- if (p == last_found)
- p = packed_git;
- else
- p = p->next;
- if (p == last_found)
- p = p->next;
- } while (p);
+ last_found_pack = p;
+ return 1;
+ }
return 0;
}
valgrind:
$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
-# Smoke testing targets
--include ../GIT-VERSION-FILE
-uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown')
-uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
-
-test-results:
- mkdir -p test-results
-
-test-results/git-smoke.tar.gz: test-results
- $(PERL_PATH) ./harness \
- --archive="test-results/git-smoke.tar.gz" \
- $(T)
-
-smoke: test-results/git-smoke.tar.gz
-
-SMOKE_UPLOAD_FLAGS =
-ifdef SMOKE_USERNAME
- SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)"
-endif
-ifdef SMOKE_COMMENT
- SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)"
-endif
-ifdef SMOKE_TAGS
- SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)"
-endif
-
-smoke_report: smoke
- curl \
- -H "Expect: " \
- -F project=Git \
- -F architecture="$(uname_M)" \
- -F platform="$(uname_S)" \
- -F revision="$(GIT_VERSION)" \
- -F report_file=@test-results/git-smoke.tar.gz \
- $(SMOKE_UPLOAD_FLAGS) \
- http://smoke.git.nix.is/app/projects/process_add_report/1 \
- | grep -v ^Redirecting
-
-.PHONY: pre-clean $(T) aggregate-results clean valgrind smoke smoke_report
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
That'll generate a detailed cover report in the "cover_db_html"
directory, which you can then copy to a webserver, or inspect locally
in a browser.
-
-Smoke testing
--------------
-
-The Git test suite has support for smoke testing. Smoke testing is
-when you submit the results of a test run to a central server for
-analysis and aggregation.
-
-Running a smoke tester is an easy and valuable way of contributing to
-Git development, particularly if you have access to an uncommon OS on
-obscure hardware.
-
-After building Git you can generate a smoke report like this in the
-"t" directory:
-
- make clean smoke
-
-You can also pass arguments via the environment. This should make it
-faster:
-
- GIT_TEST_OPTS='--root=/dev/shm' TEST_JOBS=10 make clean smoke
-
-The "smoke" target will run the Git test suite with Perl's
-"TAP::Harness" module, and package up the results in a .tar.gz archive
-with "TAP::Harness::Archive". The former is included with Perl v5.10.1
-or later, but you'll need to install the latter from the CPAN. See the
-"Test coverage" section above for how you might do that.
-
-Once the "smoke" target finishes you'll see a message like this:
-
- TAP Archive created at <path to git>/t/test-results/git-smoke.tar.gz
-
-To upload the smoke report you need to have curl(1) installed, then
-do:
-
- make smoke_report
-
-To upload the report anonymously. Hopefully that'll return something
-like "Reported #7 added.".
-
-If you're going to be uploading reports frequently please request a
-user account by E-Mailing gitsmoke@v.nix.is. Once you have a username
-and password you'll be able to do:
-
- SMOKE_USERNAME=<username> SMOKE_PASSWORD=<password> make smoke_report
-
-You can also add an additional comment to attach to the report, and/or
-a comma separated list of tags:
-
- SMOKE_USERNAME=<username> SMOKE_PASSWORD=<password> \
- SMOKE_COMMENT=<comment> SMOKE_TAGS=<tags> \
- make smoke_report
-
-Once the report is uploaded it'll be made available at
-http://smoke.git.nix.is, here's an overview of Recent Smoke Reports
-for Git:
-
- http://smoke.git.nix.is/app/projects/smoke_reports/1
-
-The reports will also be mirrored to GitHub every few hours:
-
- http://github.com/gitsmoke/smoke-reports
-
-The Smolder SQLite database is also mirrored and made available for
-download:
-
- http://github.com/gitsmoke/smoke-database
-
-Note that the database includes hashed (with crypt()) user passwords
-and E-Mail addresses. Don't use a valuable password for the smoke
-service if you have an account, or an E-Mail address you don't want to
-be publicly known. The user accounts are just meant to be convenient
-labels, they're not meant to be secure.
+++ /dev/null
-#!/usr/bin/perl
-use strict;
-use warnings;
-use Getopt::Long ();
-use TAP::Harness::Archive;
-
-Getopt::Long::Parser->new(
- config => [ qw/ pass_through / ],
-)->getoptions(
- 'jobs:1' => \(my $jobs = $ENV{TEST_JOBS}),
- 'archive=s' => \my $archive,
-) or die "$0: Couldn't getoptions()";
-
-TAP::Harness::Archive->new({
- jobs => $jobs,
- archive => $archive,
- ($ENV{GIT_TEST_OPTS}
- ? (test_args => [ split /\s+/, $ENV{GIT_TEST_OPTS} ])
- : ()),
- extra_properties => {},
-})->runtests(@ARGV);
#!/bin/sh
-test_description='checkout from unborn branch protects contents'
+test_description='checkout from unborn branch'
. ./test-lib.sh
test_expect_success 'setup' '
git checkout -b new origin
'
+test_expect_success 'checking out another branch from unborn state' '
+ git checkout --orphan newroot &&
+ git checkout -b anothername &&
+ test_must_fail git show-ref --verify refs/heads/newroot &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/anothername >expect &&
+ test_cmp expect actual
+'
+
test_done
! grep "$empty" actual
'
-test_expect_success 'cannot commit with i-t-a entry' '
+test_expect_success 'i-t-a entry is simply ignored' '
test_tick &&
git commit -a -m initial &&
git reset --hard &&
echo frotz >nitfol &&
git add rezrov &&
git add -N nitfol &&
- test_must_fail git commit -m initial
+ git commit -m second &&
+ test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
+ test $(git diff --name-only HEAD -- nitfol | wc -l) = 1
'
test_expect_success 'can commit with an unrelated i-t-a entry in index' '
git reset --hard &&
- echo xyzzy >rezrov &&
+ echo bozbar >rezrov &&
echo frotz >nitfol &&
git add rezrov &&
git add -N nitfol &&
# Copyright (c) 2005 Amos Waterland
#
-test_description='git branch --foo should not create bogus branch
+test_description='git branch assorted tests'
-This test runs git branch --help and checks that the argument is properly
-handled. Specifically, that a bogus branch is not created.
-'
. ./test-lib.sh
test_expect_success \
'
+test_expect_success 'use --edit-description' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ EDITOR=./editor git branch --edit-description &&
+ write_script editor <<-\EOF &&
+ git stripspace -s <"$1" >"EDITOR_OUTPUT"
+ EOF
+ EDITOR=./editor git branch --edit-description &&
+ echo "New contents" >expect &&
+ test_cmp EDITOR_OUTPUT expect
+'
+
+test_expect_success 'detect typo in branch name when using --edit-description' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ (
+ EDITOR=./editor &&
+ export EDITOR &&
+ test_must_fail git branch --edit-description no-such-branch
+ )
+'
+
+test_expect_success 'refuse --edit-description on unborn branch for now' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ git checkout --orphan unborn &&
+ (
+ EDITOR=./editor &&
+ export EDITOR &&
+ test_must_fail git branch --edit-description
+ )
+'
+
test_done
test "a note" = "$(git notes show HEAD)"
'
+test_expect_success 'rebase commit with an ancient timestamp' '
+ git reset --hard &&
+
+ >old.one && git add old.one && test_tick &&
+ git commit --date="@12345 +0400" -m "Old one" &&
+ >old.two && git add old.two && test_tick &&
+ git commit --date="@23456 +0500" -m "Old two" &&
+ >old.three && git add old.three && test_tick &&
+ git commit --date="@34567 +0600" -m "Old three" &&
+
+ git cat-file commit HEAD^^ >actual &&
+ grep "author .* 12345 +0400$" actual &&
+ git cat-file commit HEAD^ >actual &&
+ grep "author .* 23456 +0500$" actual &&
+ git cat-file commit HEAD >actual &&
+ grep "author .* 34567 +0600$" actual &&
+
+ git rebase --onto HEAD^^ HEAD^ &&
+
+ git cat-file commit HEAD >actual &&
+ grep "author .* 34567 +0600$" actual
+'
+
test_done
git format-patch -M --stdout lorem^ >rename-add.patch &&
# reset time
- unset test_tick &&
+ sane_unset test_tick &&
test_tick
'
read repository &&
read branch
} <digest &&
- {
- test "$branch" = full ||
- test "$branch" = master ||
- test "$branch" = for-upstream
- }
+ test "$branch" = tags/full
'
! grep "Writing objects" err
'
+test_expect_success TTY 'quiet push' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
+ test_cmp /dev/null output
+'
+
test_done
ROOT_PATH="$PWD"
LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'}
. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
start_httpd
test_expect_success 'setup remote repository' '
test_must_fail git show-ref --verify refs/remotes/origin/dev
'
+cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
+#!/bin/sh
+exit 1
+EOF
+chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+remote: error: hook declined to update refs/heads/dev2
+To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
+ ! [remote rejected] dev2 -> dev2 (hook declined)
+error: failed to push some refs to 'http://127.0.0.1:5541/smart/test_repo.git'
+EOF
+
+test_expect_success 'rejected update prints status' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev2 &&
+ : >path4 &&
+ git add path4 &&
+ test_tick &&
+ git commit -m dev2 &&
+ test_must_fail git push origin dev2 2>act &&
+ sed -e "/^remote: /s/ *$//" <act >cmp &&
+ test_cmp exp cmp
+'
+rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
cat >exp <<EOF
GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
EOF
test_expect_success 'used receive-pack service' '
sed -e "
git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
'
+test_expect_success TTY 'quiet push' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit quiet &&
+ test_terminal git push --quiet --no-progress 2>&1 | tee output &&
+ test_cmp /dev/null output
+'
+
stop_httpd
test_done
GET() {
REQUEST_METHOD="GET" && export REQUEST_METHOD &&
run_backend "/repo.git/$1" &&
- unset REQUEST_METHOD &&
+ sane_unset REQUEST_METHOD &&
if ! grep "Status" act.out >act
then
printf "Status: 200 OK\r\n" >act
REQUEST_METHOD="POST" && export REQUEST_METHOD &&
CONTENT_TYPE="application/x-$1-request" && export CONTENT_TYPE &&
run_backend "/repo.git/$1" "$2" &&
- unset REQUEST_METHOD &&
- unset CONTENT_TYPE &&
+ sane_unset REQUEST_METHOD &&
+ sane_unset CONTENT_TYPE &&
if ! grep "Status" act.out >act
then
printf "Status: 200 OK\r\n" >act
check_result 'I E C B A' --simplify-merges -- file
check_result 'I B A' -- file
check_result 'I B A' --topo-order -- file
+check_result 'H' --first-parent -- another-file
test_done
'
test_expect_success 'massive simple rename does not spam added files' '
- unset GIT_MERGE_VERBOSITY &&
+ sane_unset GIT_MERGE_VERBOSITY &&
git merge --no-stat simple-rename | grep -v Removing >output &&
test 5 -gt "$(wc -l < output)"
'
test_cmp expect actual
'
+test_expect_success 'annotations for blobs are empty' '
+ blob=$(git hash-object -w --stdin <<-\EOF
+ Blob paragraph 1.
+
+ Blob paragraph 2.
+ EOF
+ ) &&
+ git tag tag-blob $blob &&
+ echo "tag-blob " >expect &&
+ git tag -n1 -l tag-blob >actual &&
+ test_cmp expect actual
+'
+
# trying to verify annotated non-signed tags:
test_expect_success GPG \
)
'
+test_expect_success 'submodule add properly re-creates deeper level submodules' '
+ (cd super &&
+ git reset --hard master &&
+ rm -rf deeper/ &&
+ git submodule add ../submodule deeper/submodule
+ )
+'
+
test_done
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
test_cmp actual expected
'
+test_expect_success GPG 'merge --ff-only tag' '
+ git reset --hard c0 &&
+ git commit --allow-empty -m "A newer commit" &&
+ git tag -s -m "A newer commit" signed &&
+ git reset --hard c0 &&
+
+ git merge --ff-only signed &&
+ git rev-parse signed^0 >expect &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success GPG 'merge --no-edit tag should skip editor' '
+ git reset --hard c0 &&
+ git commit --allow-empty -m "A newer commit" &&
+ git tag -f -s -m "A newer commit" signed &&
+ git reset --hard c0 &&
+
+ EDITOR=false git merge --no-edit signed &&
+ git rev-parse signed^0 >expect &&
+ git rev-parse HEAD^2 >actual &&
+ test_cmp actual expect
+'
+
test_done
echo branch1 change >file1 &&
echo branch1 newfile >file2 &&
echo branch1 spaced >"spaced name" &&
+ echo branch1 both added >both &&
echo branch1 change file11 >file11 &&
echo branch1 change file13 >file13 &&
echo branch1 sub >subdir/file3 &&
git checkout -b submod-branch1
) &&
git add file1 "spaced name" file11 file13 file2 subdir/file3 submod &&
+ git add both &&
git rm file12 &&
git commit -m "branch1 changes" &&
echo master updated >file1 &&
echo master new >file2 &&
echo master updated spaced >"spaced name" &&
+ echo master both added >both &&
echo master updated file12 >file12 &&
echo master updated file14 >file14 &&
echo master new sub >subdir/file3 &&
git checkout -b submod-master
) &&
git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
+ git add both &&
git rm file11 &&
git commit -m "master updates" &&
git config merge.tool mytool &&
git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
- git config mergetool.mytool.trustExitCode true
+ git config mergetool.mytool.trustExitCode true &&
+ git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" &&
+ git config mergetool.mybase.trustExitCode true
'
test_expect_success 'custom mergetool' '
git checkout -b test1 branch1 &&
git submodule update -N &&
test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "" | git mergetool file1 file1 ) &&
( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) &&
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
( yes "" | git mergetool "spaced name" >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
cd subdir &&
( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "r" | git mergetool submod ) &&
rmdir submod && mv submod-movedaside submod &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "l" | git mergetool submod ) &&
test ! -e submod &&
test_must_fail git merge test6 &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "r" | git mergetool submod ) &&
test ! -e submod &&
test_must_fail git merge test6 &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "l" | git mergetool submod ) &&
test "$(cat submod/bar)" = "master submodule" &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "r" | git mergetool submod ) &&
rmdir submod && mv submod-movedaside submod &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "l" | git mergetool submod ) &&
git submodule update -N &&
test_must_fail git merge test7 &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "r" | git mergetool submod ) &&
test -d submod.orig &&
test_must_fail git merge test7 &&
test -n "$(git ls-files -u)" &&
( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
( yes "l" | git mergetool submod ) &&
test "$(cat submod/bar)" = "master submodule" &&
git submodule update -N
'
+test_expect_success 'file with no base' '
+ git checkout -b test13 branch1 &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool mybase -- both &&
+ >expected &&
+ test_cmp both expected &&
+ git reset --hard master >/dev/null 2>&1
+'
+
test_done
'
done
+cat >expected <<EOF
+file
+EOF
+test_expect_success 'grep -l -C' '
+ git grep -l -C1 foo >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:5
+EOF
+test_expect_success 'grep -l -C' '
+ git grep -c -C1 foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -L -C' '
+ git ls-files >expected &&
+ git grep -L -C1 nonexistent_string >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<EOF
file:foo mmap bar_mmap
EOF
rm -r "$GIT_DIR" &&
test x = x"$(git config svn.authorsfile)" &&
test_config="$HOME"/.gitconfig &&
- unset GIT_DIR &&
- unset GIT_CONFIG &&
+ sane_unset GIT_DIR &&
+ sane_unset GIT_CONFIG &&
git config --global \
svn.authorsfile "$HOME"/svn-authors &&
test x"$HOME"/svn-authors = x"$(git config svn.authorsfile)" &&
(mkdir shared &&
cd shared &&
- unset GIT_DIR &&
+ sane_unset GIT_DIR &&
cvs co . &&
git init &&
git add " space" &&
'config override: tree view, features enabled in repo config (2)' \
'gitweb_run "p=.git;a=tree"'
+# ----------------------------------------------------------------------
+# searching
+
+cat >>gitweb_config.perl <<\EOF
+
+# enable search
+$feature{'search'}{'default'} = [1];
+$feature{'grep'}{'default'} = [1];
+$feature{'pickaxe'}{'default'} = [1];
+EOF
+
+test_expect_success \
+ 'search: preparation' \
+ 'echo "1st MATCH" >>file &&
+ echo "2nd MATCH" >>file &&
+ echo "MATCH" >>bar &&
+ git add file bar &&
+ git commit -m "Added MATCH word"'
+
+test_expect_success \
+ 'search: commit author' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=author;s=A+U+Thor"'
+
+test_expect_success \
+ 'search: commit message' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=commitr;s=MATCH"'
+
+test_expect_success \
+ 'search: grep' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=grep;s=MATCH"'
+
+test_expect_success \
+ 'search: pickaxe' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=pickaxe;s=MATCH"'
+
+test_expect_success \
+ 'search: projects' \
+ 'gitweb_run "a=project_list;s=.git"'
+
# ----------------------------------------------------------------------
# non-ASCII in README.html
test_when_finished cleanup_git &&
(
P4CONFIG=p4config && export P4CONFIG &&
- unset P4PORT P4CLIENT &&
+ sane_unset P4PORT P4CLIENT &&
"$GITP4" clone --verbose --dest="$git" //depot
)
'
test_when_finished cleanup_git &&
(
P4CONFIG=p4config && export P4CONFIG &&
- unset P4PORT P4CLIENT &&
+ sane_unset P4PORT P4CLIENT &&
"$GITP4" clone --verbose --dest="git" //depot
)
'
git config "$@"
}
+
test_config_global () {
test_when_finished "test_unconfig --global '$1'" &&
git config --global "$@"
}
+write_script () {
+ {
+ echo "#!${2-"$SHELL_PATH"}" &&
+ cat
+ } >"$1" &&
+ chmod +x "$1"
+}
+
# Use test_set_prereq to tell that a particular prerequisite is available.
# The prerequisite can later be checked for in two ways:
#
write_str_in_full(debug_fd, "#S\n");
for (;;) {
struct object *o;
+ const char *features;
unsigned char sha1_buf[20];
len = packet_read_line(0, line, sizeof(line));
reset_timeout();
get_sha1_hex(line+5, sha1_buf))
die("git upload-pack: protocol error, "
"expected to get sha, not '%s'", line);
- if (strstr(line+45, "multi_ack_detailed"))
+
+ features = line + 45;
+
+ if (parse_feature_request(features, "multi_ack_detailed"))
multi_ack = 2;
- else if (strstr(line+45, "multi_ack"))
+ else if (parse_feature_request(features, "multi_ack"))
multi_ack = 1;
- if (strstr(line+45, "no-done"))
+ if (parse_feature_request(features, "no-done"))
no_done = 1;
- if (strstr(line+45, "thin-pack"))
+ if (parse_feature_request(features, "thin-pack"))
use_thin_pack = 1;
- if (strstr(line+45, "ofs-delta"))
+ if (parse_feature_request(features, "ofs-delta"))
use_ofs_delta = 1;
- if (strstr(line+45, "side-band-64k"))
+ if (parse_feature_request(features, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
- else if (strstr(line+45, "side-band"))
+ else if (parse_feature_request(features, "side-band"))
use_sideband = DEFAULT_PACKET_MAX;
- if (strstr(line+45, "no-progress"))
+ if (parse_feature_request(features, "no-progress"))
no_progress = 1;
- if (strstr(line+45, "include-tag"))
+ if (parse_feature_request(features, "include-tag"))
use_include_tag = 1;
o = lookup_object(sha1_buf);