--- /dev/null
+Git v1.9.1 Release Notes
+========================
+
+Fixes since v1.9.0
+------------------
+
+ * "git clean -d pathspec" did not use the given pathspec correctly
+ and ended up cleaning too much.
+
+ * "git difftool" misbehaved when the repository is bound to the
+ working tree with the ".git file" mechanism, where a textual file
+ ".git" tells us where it is.
+
+ * "git push" did not pay attention to branch.*.pushremote if it is
+ defined earlier than remote.pushdefault; the order of these two
+ variables in the configuration file should not matter, but it did
+ by mistake.
+
+ * Codepaths that parse timestamps in commit objects have been
+ tightened.
+
+ * "git diff --external-diff" incorrectly fed the submodule directory
+ in the working tree to the external diff driver when it knew it is
+ the same as one of the versions being compared.
+
+ * "git reset" needs to refresh the index when working in a working
+ tree (it can also be used to match the index to the HEAD in an
+ otherwise bare repository), but it failed to set up the working
+ tree properly, causing GIT_WORK_TREE to be ignored.
+
+ * "git check-attr" when working on a repository with a working tree
+ did not work well when the working tree was specified via the
+ --work-tree (and obviously with --git-dir) option.
+
+ * "merge-recursive" was broken in 1.7.7 era and stopped working in
+ an empty (temporary) working tree, when there are renames
+ involved. This has been corrected.
+
+ * "git rev-parse" was loose in rejecting command line arguments
+ that do not make sense, e.g. "--default" without the required
+ value for that option.
+
+ * include.path variable (or any variable that expects a path that
+ can use ~username expansion) in the configuration file is not a
+ boolean, but the code failed to check it.
+
+ * "git diff --quiet -- pathspec1 pathspec2" sometimes did not return
+ correct status value.
+
+ * Attempting to deepen a shallow repository by fetching over smart
+ HTTP transport failed in the protocol exchange, when no-done
+ extension was used. The fetching side waited for the list of
+ shallow boundary commits after the sending end stopped talking to
+ it.
+
+ * Allow "git cmd path/", when the 'path' is where a submodule is
+ bound to the top-level working tree, to match 'path', despite the
+ extra and unnecessary trailing slash (such a slash is often
+ given by command line completion).
--- /dev/null
+Git v1.9.2 Release Notes
+========================
+
+Fixes since v1.9.1
+------------------
+
+ * When it is not necessary to edit a commit log message (e.g. "git
+ commit -m" is given a message without specifying "-e"), we used to
+ disable the spawning of the editor by overriding GIT_EDITOR, but
+ this means all the uses of the editor, other than to edit the
+ commit log message, are also affected.
+
+ * "git status --porcelain --branch" showed its output with labels
+ "ahead/behind/gone" translated to the user's locale.
+
+ * "git mv" that moves a submodule forgot to adjust the array that
+ uses to keep track of which submodules were to be moved to update
+ its configuration.
+
+ * Length limit for the pathname used when removing a path in a deep
+ subdirectory has been removed to avoid buffer overflows.
+
+ * The test helper lib-terminal always run an actual test_expect_*
+ when included, which screwed up with the use of skil-all that may
+ have to be done later.
+
+ * "git index-pack" used a wrong variable to name the keep-file in an
+ error message when the file cannot be written or closed.
+
+ * "rebase -i" produced a broken insn sheet when the title of a commit
+ happened to contain '\n' (or ended with '\c') due to a careless use
+ of 'echo'.
+
+ * There were a few instances of 'git-foo' remaining in the
+ documentation that should have been spelled 'git foo'.
+
+ * Serving objects from a shallow repository needs to write a
+ new file to hold the temporary shallow boundaries but it was not
+ cleaned when we exit due to die() or a signal.
+
+ * When "git stash pop" stops after failing to apply the stash
+ (e.g. due to conflicting changes), the stash is not dropped. State
+ that explicitly in the output to let the users know.
+
+ * The labels in "git status" output that describe the nature of
+ conflicts (e.g. "both deleted") were limited to 20 bytes, which was
+ too short for some l10n (e.g. fr).
----------------------------------------------------------------
Lighttpd::
- Ensure that `mod_cgi`, `mod_alias, `mod_auth`, `mod_setenv` are
+ Ensure that `mod_cgi`, `mod_alias`, `mod_auth`, `mod_setenv` are
loaded, then set `GIT_PROJECT_ROOT` appropriately and redirect
all requests to the CGI:
+
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.9.0/git.html[documentation for release 1.9.0]
+* link:v1.9.1/git.html[documentation for release 1.9.1]
* release notes for
+ link:RelNotes/1.9.1.txt[1.9.1],
link:RelNotes/1.9.0.txt[1.9.0].
* link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
MERGE STRATEGIES
----------------
-The merge mechanism ('git-merge' and 'git-pull' commands) allows the
+The merge mechanism (`git merge` and `git pull` commands) allows the
backend 'merge strategies' to be chosen with `-s` option. Some strategies
can also take their own options, which can be passed by giving `-X<option>`
-arguments to 'git-merge' and/or 'git-pull'.
+arguments to `git merge` and/or `git pull`.
resolve::
This can only resolve two heads (i.e. the current branch
ancestor is found before we give up entirely.
Once the 'done' line is read from the client, the server will either
-send a final 'ACK obj-id' or it will send a 'NAK'. The server only sends
+send a final 'ACK obj-id' or it will send a 'NAK'. 'obj-id' is the object
+name of the last commit determined to be common. The server only sends
ACK after 'done' if there is at least one common base and multi_ack or
multi_ack_detailed is enabled. The server always sends NAK after 'done'
if there is no common base found.
Without multi_ack the client would have sent that c-b-a chain anyway,
interleaved with S-R-Q.
+multi_ack_detailed
+------------------
+This is an extension of multi_ack that permits client to better
+understand the server's in-memory state. See pack-protocol.txt,
+section "Packfile Negotiation" for more information.
+
+no-done
+-------
+This capability should only be used with the smart HTTP protocol. If
+multi_ack_detailed and no-done are both present, then the sender is
+free to immediately send a pack following its first "ACK obj-id ready"
+message.
+
+Without no-done in the smart HTTP protocol, the server session would
+end and the client has to make another trip to send "done" before
+the server can send the pack. no-done removes the last round and
+thus slightly reduces latency.
+
thin-pack
---------
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.9.0
+DEF_VER=v1.9.1
LF='
'
# Define DEFAULT_HELP_FORMAT to "man", "info" or "html"
# (defaults to "man") if you want to have a different default when
# "git help" is called without a parameter specifying the format.
+#
+# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
+# return NULL when it receives a bogus time_t.
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
BASIC_CFLAGS += -DXDL_FAST_HASH
endif
+ifdef GMTIME_UNRELIABLE_ERRORS
+ COMPAT_OBJS += compat/gmtime.o
+ BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
+endif
+
ifeq ($(TCLTK_PATH),)
NO_TCLTK = NoThanks
endif
-Documentation/RelNotes/1.9.0.txt
\ No newline at end of file
+Documentation/RelNotes/1.9.2.txt
\ No newline at end of file
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
- if (match_pathspec_depth(pathspec, entry->name, entry->len,
- prefix, seen))
+ if (dir_path_match(entry, pathspec, prefix, seen))
*dst++ = entry;
else if (flag & WARN_IMPLICIT_DOT)
/*
struct git_attr_check *check;
int cnt, i, doubledash, filei;
+ if (!is_bare_repository())
+ setup_work_tree();
+
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, check_attr_options,
static int post_checkout_hook(struct commit *old, struct commit *new,
int changed)
{
- return run_hook(NULL, "post-checkout",
- sha1_to_hex(old ? old->object.sha1 : null_sha1),
- sha1_to_hex(new ? new->object.sha1 : null_sha1),
- changed ? "1" : "0", NULL);
+ return run_hook_le(NULL, "post-checkout",
+ sha1_to_hex(old ? old->object.sha1 : null_sha1),
+ sha1_to_hex(new ? new->object.sha1 : null_sha1),
+ changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
* match_pathspec() for _all_ entries when
* opts->source_tree != NULL.
*/
- if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
- 0, ps_matched))
+ if (ce_path_match(ce, &opts->pathspec, ps_matched))
ce->ce_flags |= CE_MATCHED;
}
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
- int len, pos;
int matches = 0;
- const struct cache_entry *ce;
struct stat st;
const char *rel;
- /*
- * Remove the '/' at the end that directory
- * walking adds for directory entries.
- */
- len = ent->len;
- if (len && ent->name[len-1] == '/')
- len--;
- pos = cache_name_pos(ent->name, len);
- if (0 <= pos)
- continue; /* exact match */
- pos = -pos - 1;
- if (pos < active_nr) {
- ce = active_cache[pos];
- if (ce_namelen(ce) == len &&
- !memcmp(ce->name, ent->name, len))
- continue; /* Yup, this one exists unmerged */
- }
+ if (!cache_name_is_other(ent->name, ent->len))
+ continue;
if (lstat(ent->name, &st))
die_errno("Cannot lstat '%s'", ent->name);
if (pathspec.nr)
- matches = match_pathspec_depth(&pathspec, ent->name,
- len, 0, NULL);
+ matches = dir_path_match(ent, &pathspec, 0, NULL);
- if (S_ISDIR(st.st_mode)) {
- if (remove_directories || (matches == MATCHED_EXACTLY)) {
- rel = relative_path(ent->name, prefix, &buf);
- string_list_append(&del_list, rel);
- }
- } else {
- if (pathspec.nr && !matches)
- continue;
- rel = relative_path(ent->name, prefix, &buf);
- string_list_append(&del_list, rel);
- }
+ if (pathspec.nr && !matches)
+ continue;
+
+ if (S_ISDIR(st.st_mode) && !remove_directories &&
+ matches != MATCHED_EXACTLY)
+ continue;
+
+ rel = relative_path(ent->name, prefix, &buf);
+ string_list_append(&del_list, rel);
}
if (interactive && del_list.nr > 0)
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
- sha1_to_hex(sha1), "1", NULL);
+ err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
+ sha1_to_hex(sha1), "1", NULL);
if (!err && option_recursive)
err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
if (ce->ce_flags & CE_UPDATE)
continue;
- if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
+ if (!ce_path_match(ce, pattern, m))
continue;
item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
/* This checks and barfs if author is badly specified */
determine_author_info(author_ident);
- if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+ if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
return 0;
if (squash_message) {
return 0;
}
- if (run_hook(index_file, "prepare-commit-msg",
- git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+ if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+ git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
return 0;
if (use_editor) {
}
if (!no_verify &&
- run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+ run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
return 0;
}
use_editor = 0;
if (0 <= edit_flag)
use_editor = edit_flag;
- if (!use_editor)
- setenv("GIT_EDITOR", ":", 1);
/* Sanity check options */
if (amend && !current_head)
return finish_command(&proc);
}
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
+{
+ const char *hook_env[3] = { NULL };
+ char index[PATH_MAX];
+ va_list args;
+ int ret;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ hook_env[0] = index;
+
+ /*
+ * Let the hook know that no editor will be launched.
+ */
+ if (!editor_is_used)
+ hook_env[1] = "GIT_EDITOR=:";
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env, name, args);
+ va_end(args);
+
+ return ret;
+}
+
int cmd_commit(int argc, const char **argv, const char *prefix)
{
static struct wt_status s;
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
- run_hook(get_index_file(), "post-commit", NULL);
+ run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
struct notes_rewrite_cfg *cfg;
cfg = init_copy_notes_for_rewrite("amend");
else if (!too_many_loose_objects())
return 0;
- if (run_hook(NULL, "pre-auto-gc", NULL))
+ if (run_hook_le(NULL, "pre-auto-gc", NULL))
return 0;
return 1;
}
const struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+ if (!ce_path_match(ce, pathspec, NULL))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
fill_directory(&dir, pathspec);
for (i = 0; i < dir.nr; i++) {
- const char *name = dir.entries[i]->name;
- int namelen = strlen(name);
- if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
+ if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
continue;
hit |= grep_file(opt, dir.entries[i]->name);
if (hit && opt->status_only)
if (keep_fd < 0) {
if (errno != EEXIST)
die_errno(_("cannot write keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
} else {
if (keep_msg_len > 0) {
write_or_die(keep_fd, keep_msg, keep_msg_len);
}
if (close(keep_fd) != 0)
die_errno(_("cannot close written keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
report = "keep";
}
}
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
- if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched))
+ if (!dir_path_match(ent, &pathspec, len, ps_matched))
return;
fputs(tag, stdout);
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
- if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched))
+ if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
+ len, ps_matched,
+ S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
return;
if (tag && *tag && show_valid_bit &&
len = strlen(path);
if (len < max_prefix_len)
continue; /* outside of the prefix */
- if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched))
+ if (!match_pathspec(&pathspec, path, len,
+ max_prefix_len, ps_matched, 0))
continue; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
* show_recursive() rolls its own matching code and is
* generally ignorant of 'struct pathspec'. The magic mask
* cannot be lifted until it is converted to use
- * match_pathspec_depth() or tree_entry_interesting()
+ * match_pathspec() or tree_entry_interesting()
*/
parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
PATHSPEC_PREFER_CWD,
}
/* Run a post-merge hook */
- run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
+ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
strbuf_release(&reflog_message);
}
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
write_merge_msg(&msg);
- if (run_hook(get_index_file(), "prepare-commit-msg",
- git_path("MERGE_MSG"), "merge", NULL, NULL))
+ if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
+ git_path("MERGE_MSG"), "merge", NULL))
abort_commit(remoteheads, NULL);
if (0 < option_edit) {
if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
modes = xrealloc(modes,
(argc + last - first)
* sizeof(enum update_mode));
+ submodule_gitfile = xrealloc(submodule_gitfile,
+ (argc + last - first)
+ * sizeof(char *));
}
dst = add_slash(dst);
prefix_path(dst, dst_len,
path + length + 1);
modes[argc + j] = INDEX;
+ submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
}
memmove(destination + i,
destination + i + 1,
(argc - i) * sizeof(char *));
+ memmove(modes + i, modes + i + 1,
+ (argc - i) * sizeof(enum update_mode));
+ memmove(submodule_gitfile + i,
+ submodule_gitfile + i + 1,
+ (argc - i) * sizeof(char *));
i--;
}
} else
}
}
- if (shallow_update) {
- if (!checked_connectivity)
- error("BUG: run 'git fsck' for safety.\n"
- "If there are errors, try to remove "
- "the reported refs above");
- if (alt_shallow_file && *alt_shallow_file)
- unlink(alt_shallow_file);
- }
+ if (shallow_update && !checked_connectivity)
+ error("BUG: run 'git fsck' for safety.\n"
+ "If there are errors, try to remove "
+ "the reported refs above");
}
static struct command *read_head_info(struct sha1_array *shallow)
cmd->skip_update = 1;
}
}
- if (alt_shallow_file && *alt_shallow_file) {
- unlink(alt_shallow_file);
- alt_shallow_file = NULL;
- }
free(ref_status);
}
if (reset_type == NONE)
reset_type = MIXED; /* by default */
- if (reset_type != SOFT && reset_type != MIXED)
+ if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree()))
setup_work_tree();
if (reset_type == MIXED && is_bare_repository())
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
if (read_from_tree(&pathspec, sha1))
return 1;
- refresh_index(&the_index, flags, NULL, NULL,
- _("Unstaged changes after reset:"));
+ if (get_git_work_tree())
+ refresh_index(&the_index, flags, NULL, NULL,
+ _("Unstaged changes after reset:"));
} else {
int err = reset_index(sha1, reset_type, quiet);
if (reset_type == KEEP && !err)
continue;
}
if (!strcmp(arg, "--default")) {
- def = argv[i+1];
- i++;
+ def = argv[++i];
+ if (!def)
+ die("--default requires an argument");
continue;
}
if (!strcmp(arg, "--prefix")) {
- prefix = argv[i+1];
+ prefix = argv[++i];
+ if (!prefix)
+ die("--prefix requires an argument");
startup_info->prefix = prefix;
output_prefix = 1;
- i++;
continue;
}
if (!strcmp(arg, "--revs-only")) {
continue;
}
if (!strcmp(arg, "--resolve-git-dir")) {
- const char *gitdir = resolve_gitdir(argv[i+1]);
+ const char *gitdir = argv[++i];
if (!gitdir)
- die("not a gitdir '%s'", argv[i+1]);
+ die("--resolve-git-dir requires an argument");
+ gitdir = resolve_gitdir(gitdir);
+ if (!gitdir)
+ die("not a gitdir '%s'", argv[i]);
puts(gitdir);
continue;
}
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
+ if (!ce_path_match(ce, &pathspec, seen))
continue;
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
list.entry[list.nr].name = ce->name;
#include "resolve-undo.h"
#include "parse-options.h"
#include "pathspec.h"
+#include "dir.h"
/*
* Default to not allowing changes to the list of files. The
struct cache_entry *old = NULL;
int save_nr;
- if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
+ if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL))
continue;
if (has_head)
old = read_one_ent(NULL, head_sha1,
#define ADD_CACHE_IMPLICIT_DOT 32 /* internal to "git add -u/-A" */
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
extern int add_file_to_index(struct index_state *, const char *path, int flags);
-extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
+extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
extern int index_name_is_other(const struct index_state *, const char *, int);
extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
#define CE_MATCH_RACY_IS_DIRTY 02
/* do stat comparison even if CE_SKIP_WORKTREE is true */
#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
+/* ignore non-existent files during stat update */
+#define CE_MATCH_IGNORE_MISSING 0x08
+/* enable stat refresh */
+#define CE_MATCH_REFRESH 0x10
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
-
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
enum date_mode parse_date_format(const char *format);
+int date_overflows(unsigned long date);
#define IDENT_STRICT 1
#define IDENT_NO_DATE 2
extern void setup_alternate_shallow(struct lock_file *shallow_lock,
const char **alternate_shallow_file,
const struct sha1_array *extra);
-extern char *setup_temporary_shallow(const struct sha1_array *extra);
+extern const char *setup_temporary_shallow(const struct sha1_array *extra);
extern void advertise_shallow_grafts(int);
struct shallow_info {
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+LAST_ARG_MUST_BE_NULL
+extern int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
+
#endif /* COMMIT_H */
--- /dev/null
+#include "../git-compat-util.h"
+#undef gmtime
+#undef gmtime_r
+
+struct tm *git_gmtime(const time_t *timep)
+{
+ static struct tm result;
+ return git_gmtime_r(timep, &result);
+}
+
+struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *ret;
+
+ memset(result, 0, sizeof(*result));
+ ret = gmtime_r(timep, result);
+
+ /*
+ * Rather than NULL, FreeBSD gmtime simply leaves the "struct tm"
+ * untouched when it encounters overflow. Since "mday" cannot otherwise
+ * be zero, we can test this very quickly.
+ */
+ if (ret && !ret->tm_mday) {
+ ret = NULL;
+ errno = EOVERFLOW;
+ }
+
+ return ret;
+}
{
int ret = 0;
struct strbuf buf = STRBUF_INIT;
- char *expanded = expand_user_path(path);
+ char *expanded;
+ if (!path)
+ return config_error_nonbool("include.path");
+
+ expanded = expand_user_path(path);
if (!expanded)
return error("Could not expand include path '%s'", path);
path = expanded;
endif
PYTHON_PATH = /usr/local/bin/python
HAVE_PATHS_H = YesPlease
+ GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
tz = local_tzoffset(time);
tm = time_to_tm(time, tz);
- if (!tm)
- return NULL;
+ if (!tm) {
+ tm = time_to_tm(0, 0);
+ tz = 0;
+ }
strbuf_reset(&timebuf);
if (mode == DATE_SHORT)
gettimeofday(&tv, NULL);
return approxidate_str(date, &tv, error_ret);
}
+
+int date_overflows(unsigned long t)
+{
+ time_t sys;
+
+ /* If we overflowed our unsigned long, that's bad... */
+ if (t == ULONG_MAX)
+ return 1;
+
+ /*
+ * ...but we also are going to feed the result to system
+ * functions that expect time_t, which is often "signed long".
+ * Make sure that we fit into time_t, as well.
+ */
+ sys = t;
+ return t != sys || (t < 1) != (sys < 1);
+}
#include "unpack-trees.h"
#include "refs.h"
#include "submodule.h"
+#include "dir.h"
/*
* diff-files
if (diff_can_quit_early(&revs->diffopt))
break;
- if (!ce_path_match(ce, &revs->prune_data))
+ if (!ce_path_match(ce, &revs->prune_data, NULL))
continue;
if (ce_stage(ce)) {
if (tree == o->df_conflict_entry)
tree = NULL;
- if (ce_path_match(idx ? idx : tree, &revs->prune_data)) {
+ if (ce_path_match(idx ? idx : tree, &revs->prune_data, NULL)) {
do_oneway_diff(o, idx, tree);
if (diff_can_quit_early(&revs->diffopt)) {
o->exiting_early = 1;
i++;
else {
j = diff_opt_parse(&revs->diffopt, argv + i, argc - i);
- if (!j)
+ if (j <= 0)
die("invalid diff option/value: %s", argv[i]);
i += j;
}
remove_tempfile_installed = 1;
}
- if (!one->sha1_valid ||
- reuse_worktree_file(name, one->sha1, 1)) {
+ if (!S_ISGITLINK(one->mode) &&
+ (!one->sha1_valid ||
+ reuse_worktree_file(name, one->sha1, 1))) {
struct stat st;
if (lstat(name, &st) < 0) {
if (errno == ENOENT)
return !memcmp(one->data, two->data, one->size);
}
+static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+{
+ if (p->done_skip_stat_unmatch)
+ return p->skip_stat_unmatch_result;
+
+ p->done_skip_stat_unmatch = 1;
+ p->skip_stat_unmatch_result = 0;
+ /*
+ * 1. Entries that come from stat info dirtiness
+ * always have both sides (iow, not create/delete),
+ * one side of the object name is unknown, with
+ * the same mode and size. Keep the ones that
+ * do not match these criteria. They have real
+ * differences.
+ *
+ * 2. At this point, the file is known to be modified,
+ * with the same mode and size, and the object
+ * name of one side is unknown. Need to inspect
+ * the identical contents.
+ */
+ if (!DIFF_FILE_VALID(p->one) || /* (1) */
+ !DIFF_FILE_VALID(p->two) ||
+ (p->one->sha1_valid && p->two->sha1_valid) ||
+ (p->one->mode != p->two->mode) ||
+ diff_populate_filespec(p->one, 1) ||
+ diff_populate_filespec(p->two, 1) ||
+ (p->one->size != p->two->size) ||
+ !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ p->skip_stat_unmatch_result = 1;
+ return p->skip_stat_unmatch_result;
+}
+
static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
{
int i;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- /*
- * 1. Entries that come from stat info dirtiness
- * always have both sides (iow, not create/delete),
- * one side of the object name is unknown, with
- * the same mode and size. Keep the ones that
- * do not match these criteria. They have real
- * differences.
- *
- * 2. At this point, the file is known to be modified,
- * with the same mode and size, and the object
- * name of one side is unknown. Need to inspect
- * the identical contents.
- */
- if (!DIFF_FILE_VALID(p->one) || /* (1) */
- !DIFF_FILE_VALID(p->two) ||
- (p->one->sha1_valid && p->two->sha1_valid) ||
- (p->one->mode != p->two->mode) ||
- diff_populate_filespec(p->one, 1) ||
- diff_populate_filespec(p->two, 1) ||
- (p->one->size != p->two->size) ||
- !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ if (diff_filespec_check_stat_unmatch(p))
diff_q(&outq, p);
else {
/*
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
struct diff_filespec *one, *two;
+ struct diff_filepair *p;
if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
is_submodule_ignored(concatpath, options))
fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
one->dirty_submodule = old_dirty_submodule;
two->dirty_submodule = new_dirty_submodule;
+ p = diff_queue(&diff_queued_diff, one, two);
- diff_queue(&diff_queued_diff, one, two);
- if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+ return;
+
+ if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+ !diff_filespec_check_stat_unmatch(p))
+ return;
+
+ DIFF_OPT_SET(options, HAS_CHANGES);
}
struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
unsigned broken_pair : 1;
unsigned renamed_pair : 1;
unsigned is_unmerged : 1;
+ unsigned done_skip_stat_unmatch : 1;
+ unsigned skip_stat_unmatch_result : 1;
};
#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
return 1;
}
+#define DO_MATCH_EXCLUDE 1
+#define DO_MATCH_DIRECTORY 2
+
/*
* Does 'match' match the given name?
* A match is found if
* It returns 0 when there is no match.
*/
static int match_pathspec_item(const struct pathspec_item *item, int prefix,
- const char *name, int namelen)
+ const char *name, int namelen, unsigned flags)
{
/* name/namelen has prefix cut off by caller */
const char *match = item->match + prefix;
* The normal call pattern is:
* 1. prefix = common_prefix_len(ps);
* 2. prune something, or fill_directory
- * 3. match_pathspec_depth()
+ * 3. match_pathspec()
*
* 'prefix' at #1 may be shorter than the command's prefix and
* it's ok for #2 to match extra files. Those extras will be
if (match[matchlen-1] == '/' || name[matchlen] == '/')
return MATCHED_RECURSIVELY;
- }
+ } else if ((flags & DO_MATCH_DIRECTORY) &&
+ match[matchlen - 1] == '/' &&
+ namelen == matchlen - 1 &&
+ !ps_strncmp(item, match, name, namelen))
+ return MATCHED_EXACTLY;
if (item->nowildcard_len < item->len &&
!git_fnmatch(item, match, name,
* pathspec did not match any names, which could indicate that the
* user mistyped the nth pathspec.
*/
-static int match_pathspec_depth_1(const struct pathspec *ps,
- const char *name, int namelen,
- int prefix, char *seen,
- int exclude)
+static int do_match_pathspec(const struct pathspec *ps,
+ const char *name, int namelen,
+ int prefix, char *seen,
+ unsigned flags)
{
- int i, retval = 0;
+ int i, retval = 0, exclude = flags & DO_MATCH_EXCLUDE;
GUARD_PATHSPEC(ps,
PATHSPEC_FROMTOP |
*/
if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE)
seen[i] = MATCHED_FNMATCH;
- how = match_pathspec_item(ps->items+i, prefix, name, namelen);
+ how = match_pathspec_item(ps->items+i, prefix, name,
+ namelen, flags);
if (ps->recursive &&
(ps->magic & PATHSPEC_MAXDEPTH) &&
ps->max_depth != -1 &&
return retval;
}
-int match_pathspec_depth(const struct pathspec *ps,
- const char *name, int namelen,
- int prefix, char *seen)
+int match_pathspec(const struct pathspec *ps,
+ const char *name, int namelen,
+ int prefix, char *seen, int is_dir)
{
int positive, negative;
- positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);
+ unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
+ positive = do_match_pathspec(ps, name, namelen,
+ prefix, seen, flags);
if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
return positive;
- negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);
+ negative = do_match_pathspec(ps, name, namelen,
+ prefix, seen,
+ flags | DO_MATCH_EXCLUDE);
return negative ? 0 : positive;
}
extern int simple_length(const char *match);
extern int no_wildcard(const char *string);
extern char *common_prefix(const struct pathspec *pathspec);
-extern int match_pathspec_depth(const struct pathspec *pathspec,
- const char *name, int namelen,
- int prefix, char *seen);
+extern int match_pathspec(const struct pathspec *pathspec,
+ const char *name, int namelen,
+ int prefix, char *seen, int is_dir);
extern int within_depth(const char *name, int namelen, int depth, int max_depth);
extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec);
const char *pattern, const char *string,
int prefix);
+static inline int ce_path_match(const struct cache_entry *ce,
+ const struct pathspec *pathspec,
+ char *seen)
+{
+ return match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen,
+ S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
+}
+
+static inline int dir_path_match(const struct dir_entry *ent,
+ const struct pathspec *pathspec,
+ int prefix, char *seen)
+{
+ int has_trailing_dir = ent->len && ent->name[ent->len - 1] == '/';
+ int len = has_trailing_dir ? ent->len - 1 : ent->len;
+ return match_pathspec(pathspec, ent->name, len, prefix, seen,
+ has_trailing_dir);
+}
+
#endif
free(buf);
}
-static void remove_subtree(const char *path)
+static void remove_subtree(struct strbuf *path)
{
- DIR *dir = opendir(path);
+ DIR *dir = opendir(path->buf);
struct dirent *de;
- char pathbuf[PATH_MAX];
- char *name;
+ int origlen = path->len;
if (!dir)
- die_errno("cannot opendir '%s'", path);
- strcpy(pathbuf, path);
- name = pathbuf + strlen(path);
- *name++ = '/';
+ die_errno("cannot opendir '%s'", path->buf);
while ((de = readdir(dir)) != NULL) {
struct stat st;
+
if (is_dot_or_dotdot(de->d_name))
continue;
- strcpy(name, de->d_name);
- if (lstat(pathbuf, &st))
- die_errno("cannot lstat '%s'", pathbuf);
+
+ strbuf_addch(path, '/');
+ strbuf_addstr(path, de->d_name);
+ if (lstat(path->buf, &st))
+ die_errno("cannot lstat '%s'", path->buf);
if (S_ISDIR(st.st_mode))
- remove_subtree(pathbuf);
- else if (unlink(pathbuf))
- die_errno("cannot unlink '%s'", pathbuf);
+ remove_subtree(path);
+ else if (unlink(path->buf))
+ die_errno("cannot unlink '%s'", path->buf);
+ strbuf_setlen(path, origlen);
}
closedir(dir);
- if (rmdir(path))
- die_errno("cannot rmdir '%s'", path);
+ if (rmdir(path->buf))
+ die_errno("cannot rmdir '%s'", path->buf);
}
static int create_file(const char *path, unsigned int mode)
int checkout_entry(struct cache_entry *ce,
const struct checkout *state, char *topath)
{
- static struct strbuf path_buf = STRBUF_INIT;
- char *path;
+ static struct strbuf path = STRBUF_INIT;
struct stat st;
- int len;
if (topath)
return write_entry(ce, topath, state, 1);
- strbuf_reset(&path_buf);
- strbuf_add(&path_buf, state->base_dir, state->base_dir_len);
- strbuf_add(&path_buf, ce->name, ce_namelen(ce));
- path = path_buf.buf;
- len = path_buf.len;
+ strbuf_reset(&path);
+ strbuf_add(&path, state->base_dir, state->base_dir_len);
+ strbuf_add(&path, ce->name, ce_namelen(ce));
- if (!check_path(path, len, &st, state->base_dir_len)) {
+ if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
if (!changed)
return 0;
if (!state->force) {
if (!state->quiet)
- fprintf(stderr, "%s already exists, no checkout\n", path);
+ fprintf(stderr,
+ "%s already exists, no checkout\n",
+ path.buf);
return -1;
}
if (S_ISGITLINK(ce->ce_mode))
return 0;
if (!state->force)
- return error("%s is a directory", path);
- remove_subtree(path);
- } else if (unlink(path))
- return error("unable to unlink old '%s' (%s)", path, strerror(errno));
+ return error("%s is a directory", path.buf);
+ remove_subtree(&path);
+ } else if (unlink(path.buf))
+ return error("unable to unlink old '%s' (%s)",
+ path.buf, strerror(errno));
} else if (state->not_new)
return 0;
- create_directories(path, len, state);
- return write_entry(ce, path, state, 0);
+
+ create_directories(path.buf, path.len, state);
+ return write_entry(ce, path.buf, state, 0);
}
}
strbuf_release(&req_buf);
- consume_shallow_list(args, fd[0]);
+ if (!got_ready || !no_done)
+ consume_shallow_list(args, fd[0]);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
if (!si->shallow || !si->shallow->nr)
return;
- if (alternate_shallow_file) {
- /*
- * The temporary shallow file is only useful for
- * index-pack and unpack-objects because it may
- * contain more roots than we want. Delete it.
- */
- if (*alternate_shallow_file)
- unlink(alternate_shallow_file);
- free((char *)alternate_shallow_file);
- }
-
if (args->cloning) {
/*
* remote is shallow, but this is a clone, there are
static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
{
+ char *end;
+
if (**ident == '<')
return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
*ident += strcspn(*ident, "<>\n");
(*ident)++;
if (**ident == '0' && (*ident)[1] != ' ')
return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
- *ident += strspn(*ident, "0123456789");
- if (**ident != ' ')
+ if (date_overflows(strtoul(*ident, &end, 10)))
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow");
+ if (end == *ident || *end != ' ')
return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
- (*ident)++;
+ *ident = end + 1;
if ((**ident != '+' && **ident != '-') ||
!isdigit((*ident)[1]) ||
!isdigit((*ident)[2]) ||
int parents = 0;
int err;
- if (commit->date == ULONG_MAX)
- return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
-
if (memcmp(buffer, "tree ", 5))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
/* Get the passwd entry for the UID of the current process. */
struct passwd *xgetpwuid_self(void);
+#ifdef GMTIME_UNRELIABLE_ERRORS
+struct tm *git_gmtime(const time_t *);
+struct tm *git_gmtime_r(const time_t *, struct tm *);
+#define gmtime git_gmtime
+#define gmtime_r git_gmtime_r
+#endif
+
#endif
sub find_worktree
{
- my ($repo) = @_;
-
# Git->repository->wc_path() does not honor changes to the working
# tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
# config variable.
- my $worktree;
- my $env_worktree = $ENV{GIT_WORK_TREE};
- my $core_worktree = Git::config('core.worktree');
-
- if (defined($env_worktree) and (length($env_worktree) > 0)) {
- $worktree = $env_worktree;
- } elsif (defined($core_worktree) and (length($core_worktree) > 0)) {
- $worktree = $core_worktree;
- } else {
- $worktree = $repo->wc_path();
- }
-
- return $worktree;
+ return Git::command_oneline('rev-parse', '--show-toplevel');
}
sub print_tool_help
my $rc;
my $error = 0;
my $repo = Git->repository();
- my $workdir = find_worktree($repo);
+ my $workdir = find_worktree();
my ($a, $b, $tmpdir, @worktree) =
setup_dir_diff($repo, $workdir, $symlinks);
;;
esac
done
- echo "$sha1 $action $prefix $rest"
+ printf '%s %s %s %s\n' "$sha1" "$action" "$prefix" "$rest"
# if it's a single word, try to resolve to a full sha1 and
# emit a second copy. This allows us to match on both message
# and on sha1 prefix
pop_stash() {
assert_stash_ref "$@"
- apply_stash "$@" &&
- drop_stash "$@"
+ if apply_stash "$@"
+ then
+ drop_stash "$@"
+ else
+ status=$?
+ say "The stash is kept in case you need it again."
+ exit $status
+ fi
}
drop_stash () {
const char *path, int stage, int refresh, int options)
{
struct cache_entry *ce;
- ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+ ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage,
+ (refresh ? (CE_MATCH_REFRESH |
+ CE_MATCH_IGNORE_MISSING) : 0 ));
if (!ce)
return error(_("addinfo_cache failed for path '%s'"), path);
return add_cache_entry(ce, options);
char *expand_user_path(const char *path)
{
struct strbuf user_path = STRBUF_INIT;
- const char *first_slash = strchrnul(path, '/');
const char *to_copy = path;
if (path == NULL)
goto return_null;
if (path[0] == '~') {
+ const char *first_slash = strchrnul(path, '/');
const char *username = path + 1;
size_t username_len = first_slash - username;
if (username_len == 0) {
return;
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
+ ce_path_match(ce, pathspec, seen);
}
}
#: remote.c:1875
msgid " (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr " (utilisez \"git branch -unset-upstream\" pour corriger)\n"
+msgstr " (utilisez \"git branch --unset-upstream\" pour corriger)\n"
#: remote.c:1878
#, c-format
#: builtin/branch.c:1027
#, c-format
msgid " git branch --set-upstream-to %s\n"
-msgstr " git branch -set-upstream-to %s\n"
+msgstr " git branch --set-upstream-to %s\n"
#: builtin/bundle.c:47
#, c-format
*/
#include "cache.h"
#include "pathspec.h"
+#include "dir.h"
#ifdef NO_PTHREADS
static void preload_index(struct index_state *index,
continue;
if (ce_uptodate(ce))
continue;
- if (!ce_path_match(ce, &p->pathspec))
+ if (!ce_path_match(ce, &p->pathspec, NULL))
continue;
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
continue;
enum date_mode mode)
{
unsigned long date = 0;
- int tz = 0;
+ long tz = 0;
if (ident->date_begin && ident->date_end)
date = strtoul(ident->date_begin, NULL, 10);
- if (ident->tz_begin && ident->tz_end)
- tz = strtol(ident->tz_begin, NULL, 10);
+ if (date_overflows(date))
+ date = 0;
+ else {
+ if (ident->tz_begin && ident->tz_end)
+ tz = strtol(ident->tz_begin, NULL, 10);
+ if (tz >= INT_MAX || tz <= INT_MIN)
+ tz = 0;
+ }
return show_date(date, tz, mode);
}
#include "strbuf.h"
#include "varint.h"
-static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
+ unsigned int options);
/* Mask for the name length in ce_flags in the on-disk index */
struct cache_entry *make_cache_entry(unsigned int mode,
const unsigned char *sha1, const char *path, int stage,
- int refresh)
+ unsigned int refresh_options)
{
int size, len;
struct cache_entry *ce;
ce->ce_namelen = len;
ce->ce_mode = create_ce_mode(mode);
- if (refresh)
- return refresh_cache_entry(ce, 0);
-
- return ce;
+ return refresh_cache_entry(ce, refresh_options);
}
int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
}
-int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
-{
- return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
-}
-
/*
* We fundamentally don't like some paths: we don't want
* dot or dot-dot anywhere, and for obvious reasons don't
struct stat st;
struct cache_entry *updated;
int changed, size;
+ int refresh = options & CE_MATCH_REFRESH;
int ignore_valid = options & CE_MATCH_IGNORE_VALID;
int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
+ int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
- if (ce_uptodate(ce))
+ if (!refresh || ce_uptodate(ce))
return ce;
/*
}
if (lstat(ce->name, &st) < 0) {
+ if (ignore_missing && errno == ENOENT)
+ return ce;
if (err)
*err = errno;
return NULL;
int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
int first = 1;
int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
- unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
+ unsigned int options = (CE_MATCH_REFRESH |
+ (really ? CE_MATCH_IGNORE_VALID : 0) |
+ (not_new ? CE_MATCH_IGNORE_MISSING : 0));
const char *modified_fmt;
const char *deleted_fmt;
const char *typechange_fmt;
if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
continue;
- if (pathspec &&
- !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
+ if (pathspec && !ce_path_match(ce, pathspec, seen))
filtered = 1;
if (ce_stage(ce)) {
if (!new) {
const char *fmt;
- if (not_new && cache_errno == ENOENT)
- continue;
if (really && cache_errno == EINVAL) {
/* If we are doing --really-refresh that
* means the index is not valid anymore.
return has_errors;
}
-static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
+ unsigned int options)
{
- return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
+ return refresh_cache_ent(&the_index, ce, options, NULL, NULL);
}
static struct branch *current_branch;
static const char *default_remote_name;
+static const char *branch_pushremote_name;
static const char *pushremote_name;
static int explicit_default_remote_name;
}
} else if (!strcmp(subkey, ".pushremote")) {
if (branch == current_branch)
- if (git_config_string(&pushremote_name, key, value))
+ if (git_config_string(&branch_pushremote_name, key, value))
return -1;
} else if (!strcmp(subkey, ".merge")) {
if (!value)
make_branch(head_ref + strlen("refs/heads/"), 0);
}
git_config(handle_config, NULL);
+ if (branch_pushremote_name) {
+ free((char *)pushremote_name);
+ pushremote_name = branch_pushremote_name;
+ }
alias_all_urls();
}
find_conflict(&conflict);
for (i = 0; i < conflict.nr; i++) {
struct string_list_item *it = &conflict.items[i];
- if (!match_pathspec_depth(pathspec, it->string, strlen(it->string),
- 0, NULL))
+ if (!match_pathspec(pathspec, it->string,
+ strlen(it->string), 0, NULL, 0))
continue;
rerere_forget_one_path(it->string, &merge_rr);
}
for (i = 0; i < istate->cache_nr; i++) {
const struct cache_entry *ce = istate->cache[i];
- if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+ if (!ce_path_match(ce, pathspec, NULL))
continue;
i = unmerge_index_entry_at(istate, i);
}
#include "line-log.h"
#include "mailmap.h"
#include "commit-slab.h"
+#include "dir.h"
volatile show_early_output_fn_t show_early_output;
const struct cache_entry *ce = active_cache[i];
if (!ce_stage(ce))
continue;
- if (ce_path_match(ce, &revs->prune_data)) {
+ if (ce_path_match(ce, &revs->prune_data, NULL)) {
prune_num++;
prune = xrealloc(prune, sizeof(*prune) * prune_num);
prune[prune_num-2] = ce->name;
return path;
}
-int run_hook(const char *index_file, const char *name, ...)
+int run_hook_ve(const char *const *env, const char *name, va_list args)
{
struct child_process hook;
struct argv_array argv = ARGV_ARRAY_INIT;
- const char *p, *env[2];
- char index[PATH_MAX];
- va_list args;
+ const char *p;
int ret;
p = find_hook(name);
argv_array_push(&argv, p);
- va_start(args, name);
while ((p = va_arg(args, const char *)))
argv_array_push(&argv, p);
- va_end(args);
memset(&hook, 0, sizeof(hook));
hook.argv = argv.argv;
+ hook.env = env;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
- if (index_file) {
- snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
- env[0] = index;
- env[1] = NULL;
- hook.env = env;
- }
ret = run_command(&hook);
argv_array_clear(&argv);
return ret;
}
+
+int run_hook_le(const char *const *env, const char *name, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, name);
+ ret = run_hook_ve(env, name, args);
+ va_end(args);
+
+ return ret;
+}
+
+int run_hook_with_custom_index(const char *index_file, const char *name, ...)
+{
+ const char *hook_env[3] = { NULL };
+ char index[PATH_MAX];
+ va_list args;
+ int ret;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ hook_env[0] = index;
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env, name, args);
+ va_end(args);
+
+ return ret;
+}
extern char *find_hook(const char *name);
LAST_ARG_MUST_BE_NULL
-extern int run_hook(const char *index_file, const char *name, ...);
+extern int run_hook_le(const char *const *env, const char *name, ...);
+extern int run_hook_ve(const char *const *env, const char *name, va_list args);
+
+LAST_ARG_MUST_BE_NULL
+__attribute__((deprecated))
+extern int run_hook_with_custom_index(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#include "diff.h"
#include "revision.h"
#include "commit-slab.h"
+#include "sigchain.h"
static int is_shallow = -1;
-static struct stat shallow_stat;
+static struct stat_validity shallow_stat;
static char *alternate_shallow_file;
void set_alternate_shallow_file(const char *path, int override)
* shallow file should be used. We could just open it and it
* will likely fail. But let's do an explicit check instead.
*/
- if (!*path ||
- stat(path, &shallow_stat) ||
- (fp = fopen(path, "r")) == NULL) {
+ if (!*path || (fp = fopen(path, "r")) == NULL) {
+ stat_validity_clear(&shallow_stat);
is_shallow = 0;
return is_shallow;
}
+ stat_validity_update(&shallow_stat, fileno(fp));
is_shallow = 1;
while (fgets(buf, sizeof(buf), fp)) {
void check_shallow_file_for_update(void)
{
- struct stat st;
-
- if (!is_shallow)
- return;
- else if (is_shallow == -1)
+ if (is_shallow == -1)
die("BUG: shallow must be initialized by now");
- if (stat(git_path("shallow"), &st))
- die("shallow file was removed during fetch");
- else if (st.st_mtime != shallow_stat.st_mtime
-#ifdef USE_NSEC
- || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
-#endif
- )
- die("shallow file was changed during fetch");
+ if (!stat_validity_check(&shallow_stat, git_path("shallow")))
+ die("shallow file has changed since we read it");
}
#define SEEN_ONLY 1
return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
}
-char *setup_temporary_shallow(const struct sha1_array *extra)
+static struct strbuf temporary_shallow = STRBUF_INIT;
+
+static void remove_temporary_shallow(void)
+{
+ if (temporary_shallow.len) {
+ unlink_or_warn(temporary_shallow.buf);
+ strbuf_reset(&temporary_shallow);
+ }
+}
+
+static void remove_temporary_shallow_on_signal(int signo)
+{
+ remove_temporary_shallow();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
+const char *setup_temporary_shallow(const struct sha1_array *extra)
{
+ static int installed_handler;
struct strbuf sb = STRBUF_INIT;
int fd;
+ if (temporary_shallow.len)
+ die("BUG: attempt to create two temporary shallow files");
+
if (write_shallow_commits(&sb, 0, extra)) {
- struct strbuf path = STRBUF_INIT;
- strbuf_addstr(&path, git_path("shallow_XXXXXX"));
- fd = xmkstemp(path.buf);
+ strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
+ fd = xmkstemp(temporary_shallow.buf);
+
+ if (!installed_handler) {
+ atexit(remove_temporary_shallow);
+ sigchain_push_common(remove_temporary_shallow_on_signal);
+ }
+
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
- path.buf);
+ temporary_shallow.buf);
close(fd);
strbuf_release(&sb);
- return strbuf_detach(&path, NULL);
+ return temporary_shallow.buf;
}
/*
* is_repository_shallow() sees empty string as "no shallow
* file".
*/
- return xstrdup("");
+ return temporary_shallow.buf;
}
void setup_alternate_shallow(struct lock_file *shallow_lock,
struct strbuf sb = STRBUF_INIT;
int fd;
- check_shallow_file_for_update();
fd = hold_lock_file_for_update(shallow_lock, git_path("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)
die_errno("failed to write to %s",
strbuf_release(&sb);
return;
}
- check_shallow_file_for_update();
fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
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)
die_errno("failed to write to %s",
esac
LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}}
TEST_PATH="$TEST_DIRECTORY"/lib-httpd
HTTPD_ROOT_PATH="$PWD"/httpd
# Helpers for terminal output tests.
-test_expect_success PERL 'set up terminal for tests' '
+# Catch tests which should depend on TTY but forgot to. There's no need
+# to aditionally check that the TTY prereq is set here. If the test declared
+# it and we are running the test, then it must have been set.
+test_terminal () {
+ if ! test_declared_prereq TTY
+ then
+ echo >&4 "test_terminal: need to declare TTY prerequisite"
+ return 127
+ fi
+ perl "$TEST_DIRECTORY"/test-terminal.perl "$@"
+}
+
+test_lazy_prereq TTY '
+ test_have_prereq PERL &&
+
# Reading from the pty master seems to get stuck _sometimes_
# on Mac OS X 10.5.0, using Perl 5.10.0 or 5.8.9.
#
# After 2000 iterations or so it hangs.
# https://rt.cpan.org/Ticket/Display.html?id=65692
#
- if test "$(uname -s)" = Darwin
- then
- :
- elif
- perl "$TEST_DIRECTORY"/test-terminal.perl \
- sh -c "test -t 1 && test -t 2"
- then
- test_set_prereq TTY &&
- test_terminal () {
- if ! test_declared_prereq TTY
- then
- echo >&4 "test_terminal: need to declare TTY prerequisite"
- return 127
- fi
- perl "$TEST_DIRECTORY"/test-terminal.perl "$@"
- }
- fi
+ test "$(uname -s)" != Darwin &&
+
+ perl "$TEST_DIRECTORY"/test-terminal.perl \
+ sh -c "test -t 1 && test -t 2"
'
test_line_count = 0 err
'
+test_expect_success 'using --git-dir and --work-tree' '
+ mkdir unreal real &&
+ git init real &&
+ echo "file test=in-real" >real/.gitattributes &&
+ (
+ cd unreal &&
+ attr_check file in-real "--git-dir ../real/.git --work-tree ../real"
+ )
+'
+
test_expect_success 'setup bare' '
- git clone --bare . bare.git &&
- cd bare.git
+ git clone --bare . bare.git
'
test_expect_success 'bare repository: check that .gitattribute is ignored' '
(
- echo "f test=f"
- echo "a/i test=a/i"
- ) >.gitattributes &&
- attr_check f unspecified &&
- attr_check a/f unspecified &&
- attr_check a/c/f unspecified &&
- attr_check a/i unspecified &&
- attr_check subdir/a/i unspecified
+ cd bare.git &&
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >.gitattributes &&
+ attr_check f unspecified &&
+ attr_check a/f unspecified &&
+ attr_check a/c/f unspecified &&
+ attr_check a/i unspecified &&
+ attr_check subdir/a/i unspecified
+ )
'
test_expect_success 'bare repository: check that --cached honors index' '
- GIT_INDEX_FILE=../.git/index \
- git check-attr --cached --stdin --all <../stdin-all |
- sort >actual &&
- test_cmp ../specified-all actual
+ (
+ cd bare.git &&
+ GIT_INDEX_FILE=../.git/index \
+ git check-attr --cached --stdin --all <../stdin-all |
+ sort >actual &&
+ test_cmp ../specified-all actual
+ )
'
test_expect_success 'bare repository: test info/attributes' '
(
- echo "f test=f"
- echo "a/i test=a/i"
- ) >info/attributes &&
- attr_check f f &&
- attr_check a/f f &&
- attr_check a/c/f f &&
- attr_check a/i a/i &&
- attr_check subdir/a/i unspecified
+ cd bare.git &&
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >info/attributes &&
+ attr_check f f &&
+ attr_check a/f f &&
+ attr_check a/c/f f &&
+ attr_check a/i a/i &&
+ attr_check subdir/a/i unspecified
+ )
'
test_done
grep "error in commit $new" out
'
+# date is 2^64 + 1
+test_expect_success 'integer overflow in timestamps is reported' '
+ git cat-file commit HEAD >basis &&
+ sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
+ <basis >bad-timestamp &&
+ new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.*integer overflow" out
+'
+
test_expect_success 'tag pointing to nonexistent' '
cat >invalid-tag <<-\EOF &&
object ffffffffffffffffffffffffffffffffffffffff
git add e &&
test_tick &&
git commit -m "rename a->e" &&
+ c7=$(git rev-parse --verify HEAD) &&
git checkout rename-ln &&
git mv a e &&
test_ln_s_add e a &&
'
+test_expect_success 'merge-recursive w/ empty work tree - ours has rename' '
+ (
+ GIT_WORK_TREE="$PWD/ours-has-rename-work" &&
+ export GIT_WORK_TREE &&
+ GIT_INDEX_FILE="$PWD/ours-has-rename-index" &&
+ export GIT_INDEX_FILE &&
+ mkdir "$GIT_WORK_TREE" &&
+ git read-tree -i -m $c7 &&
+ git update-index --ignore-missing --refresh &&
+ git merge-recursive $c0 -- $c7 $c3 &&
+ git ls-files -s >actual-files
+ ) 2>actual-err &&
+ >expected-err &&
+ cat >expected-files <<-EOF &&
+ 100644 $o3 0 b/c
+ 100644 $o0 0 c
+ 100644 $o0 0 d/e
+ 100644 $o0 0 e
+ EOF
+ test_cmp expected-files actual-files &&
+ test_cmp expected-err actual-err
+'
+
+test_expect_success 'merge-recursive w/ empty work tree - theirs has rename' '
+ (
+ GIT_WORK_TREE="$PWD/theirs-has-rename-work" &&
+ export GIT_WORK_TREE &&
+ GIT_INDEX_FILE="$PWD/theirs-has-rename-index" &&
+ export GIT_INDEX_FILE &&
+ mkdir "$GIT_WORK_TREE" &&
+ git read-tree -i -m $c3 &&
+ git update-index --ignore-missing --refresh &&
+ git merge-recursive $c0 -- $c3 $c7 &&
+ git ls-files -s >actual-files
+ ) 2>actual-err &&
+ >expected-err &&
+ cat >expected-files <<-EOF &&
+ 100644 $o3 0 b/c
+ 100644 $o0 0 c
+ 100644 $o0 0 d/e
+ 100644 $o0 0 e
+ EOF
+ test_cmp expected-files actual-files &&
+ test_cmp expected-err actual-err
+'
+
test_expect_success 'merge removes empty directories' '
git reset --hard master &&
test_cmp expect actual
'
+test_expect_success 'diff-cache ignores trailing slash on submodule path' '
+ git diff --name-only HEAD^ submod >expect &&
+ git diff --name-only HEAD^ submod/ >actual &&
+ test_cmp expect actual
+'
+
test_done
}
test_expect_success 'external diff with autocrlf = true' '
- git config core.autocrlf true &&
+ test_config core.autocrlf true &&
GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
'
test_expect_success 'diff --cached' '
+ test_config core.autocrlf true &&
git add file &&
git update-index --assume-unchanged file &&
echo second >file &&
test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
'
+test_expect_success 'clean up crlf leftovers' '
+ git update-index --no-assume-unchanged file &&
+ rm -f file* &&
+ git reset --hard
+'
+
+test_expect_success 'submodule diff' '
+ git init sub &&
+ ( cd sub && test_commit sub1 ) &&
+ git add sub &&
+ test_tick &&
+ git commit -m "add submodule" &&
+ ( cd sub && test_commit sub2 ) &&
+ write_script gather_pre_post.sh <<-\EOF &&
+ echo "$1 $4" # path, mode
+ cat "$2" # old file
+ cat "$5" # new file
+ EOF
+ GIT_EXTERNAL_DIFF=./gather_pre_post.sh git diff >actual &&
+ cat >expected <<-EOF &&
+ sub 160000
+ Subproject commit $(git rev-parse HEAD:sub)
+ Subproject commit $(cd sub && git rev-parse HEAD)
+ EOF
+ test_cmp expected actual
+'
+
test_done
)
'
+test_expect_success 'git diff --quiet ignores stat-change only entries' '
+ test-chmtime +10 a &&
+ echo modified >>b &&
+ test_expect_code 1 git diff --quiet
+'
+
test_done
test_cmp expect.err actual.err
'
+munge_author_date () {
+ git cat-file commit "$1" >commit.orig &&
+ sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
+ git hash-object -w -t commit commit.munge
+}
+
+test_expect_success 'unparsable dates produce sentinel value' '
+ commit=$(munge_author_date HEAD totally_bogus) &&
+ echo "Date: Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 $commit >actual.full &&
+ grep Date <actual.full >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unparsable dates produce sentinel value (%ad)' '
+ commit=$(munge_author_date HEAD totally_bogus) &&
+ echo >expect &&
+ git log -1 --format=%ad $commit >actual
+ test_cmp expect actual
+'
+
+# date is 2^64 + 1
+test_expect_success 'date parser recognizes integer overflow' '
+ commit=$(munge_author_date HEAD 18446744073709551617) &&
+ echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 --format=%ad $commit >actual &&
+ test_cmp expect actual
+'
+
+# date is 2^64 - 2
+test_expect_success 'date parser recognizes time_t overflow' '
+ commit=$(munge_author_date HEAD 18446744073709551614) &&
+ echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 --format=%ad $commit >actual &&
+ test_cmp expect actual
+'
+
+# date is within 2^63-1, but enough to choke glibc's gmtime
+test_expect_success 'absurdly far-in-future date' '
+ commit=$(munge_author_date HEAD 999999999999999999) &&
+ git log -1 --format=%ad $commit
+'
+
test_done
check_push_result down_repo $the_commit heads/master
'
+test_expect_success 'branch.*.pushremote config order is irrelevant' '
+ mk_test one_repo heads/master &&
+ mk_test two_repo heads/master &&
+ test_config remote.one.url one_repo &&
+ test_config remote.two.url two_repo &&
+ test_config branch.master.pushremote two_repo &&
+ test_config remote.pushdefault one_repo &&
+ test_config push.default matching &&
+ git push &&
+ check_push_result one_repo $the_first_commit heads/master &&
+ check_push_result two_repo $the_commit heads/master
+'
+
test_expect_success 'push with dry-run' '
mk_test testrepo heads/master &&
)
'
-if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
- say 'skipping remaining tests, git built without http support'
- test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'clone http repository' '
- git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git clone $HTTPD_URL/smart/repo.git clone &&
- (
- cd clone &&
- git fsck &&
- git log --format=%s origin/master >actual &&
- cat <<EOF >expect &&
-7
-6
-5
-4
-3
-EOF
- test_cmp expect actual
- )
-'
-
-stop_httpd
test_done
test_done
fi
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
--- /dev/null
+#!/bin/sh
+
+test_description='fetch/clone from a shallow clone over http'
+
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+commit() {
+ echo "$1" >tracked &&
+ git add tracked &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup shallow clone' '
+ commit 1 &&
+ commit 2 &&
+ commit 3 &&
+ commit 4 &&
+ commit 5 &&
+ commit 6 &&
+ commit 7 &&
+ git clone --no-local --depth=5 .git shallow &&
+ git config --global transfer.fsckObjects true
+'
+
+test_expect_success 'clone http repository' '
+ git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git clone $HTTPD_URL/smart/repo.git clone &&
+ (
+ cd clone &&
+ git fsck &&
+ git log --format=%s origin/master >actual &&
+ cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+# This test is tricky. We need large enough "have"s that fetch-pack
+# will put pkt-flush in between. Then we need a "have" the server
+# does not have, it'll send "ACK %s ready"
+test_expect_success 'no shallow lines after receiving ACK ready' '
+ (
+ cd shallow &&
+ for i in $(test_seq 15)
+ do
+ git checkout --orphan unrelated$i &&
+ test_commit unrelated$i &&
+ git push -q "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ refs/heads/unrelated$i:refs/heads/unrelated$i &&
+ git push -q ../clone/.git \
+ refs/heads/unrelated$i:refs/heads/unrelated$i ||
+ exit 1
+ done &&
+ git checkout master &&
+ test_commit new &&
+ git push "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" master
+ ) &&
+ (
+ cd clone &&
+ git checkout --orphan newnew &&
+ test_commit new-too &&
+ GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" git fetch --depth=2 &&
+ grep "fetch-pack< ACK .* ready" ../trace &&
+ ! grep "fetch-pack> done" ../trace
+ )
+'
+
+stop_httpd
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test WebDAV http-push
+
+This test runs various sanity checks on http-push.'
+
+. ./test-lib.sh
+
+if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
+then
+ skip_all="skipping test, USE_CURL_MULTI is not defined"
+ test_done
+fi
+
+LIB_HTTPD_DAV=t
+. "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
+start_httpd
+
+test_expect_success 'setup remote repository' '
+ cd "$ROOT_PATH" &&
+ mkdir test_repo &&
+ cd test_repo &&
+ git init &&
+ : >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -m initial &&
+ cd - &&
+ git clone --bare test_repo test_repo.git &&
+ cd test_repo.git &&
+ git --bare update-server-info &&
+ mv hooks/post-update.sample hooks/post-update &&
+ ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+ cd - &&
+ mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+test_expect_success 'create password-protected repository' '
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
+ cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
+'
+
+setup_askpass_helper
+
+test_expect_success 'clone remote repository' '
+ cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository with packed refs' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -m path2 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ git push &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+ git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ rm packed-refs &&
+ git update-ref refs/heads/master $ORIG_HEAD &&
+ git --bare update-server-info) &&
+ git push &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'http-push fetches unpacked objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
+
+ git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
+ "$ROOT_PATH"/fetch_unpacked &&
+
+ # By reset, we force git to retrieve the object
+ (cd "$ROOT_PATH"/fetch_unpacked &&
+ git reset --hard HEAD^ &&
+ git remote rm origin &&
+ git reflog expire --expire=0 --all &&
+ git prune &&
+ git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
+'
+
+test_expect_success 'http-push fetches packed objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+
+ git clone $HTTPD_URL/dumb/test_repo_packed.git \
+ "$ROOT_PATH"/test_repo_clone_packed &&
+
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+ git --bare repack &&
+ git --bare prune-packed) &&
+
+ # By reset, we force git to retrieve the packed object
+ (cd "$ROOT_PATH"/test_repo_clone_packed &&
+ git reset --hard HEAD^ &&
+ git remote remove origin &&
+ git reflog expire --expire=0 --all &&
+ git prune &&
+ git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
+'
+
+test_expect_success 'create and delete remote branch' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev &&
+ : >path3 &&
+ git add path3 &&
+ test_tick &&
+ git commit -m dev &&
+ git push origin dev &&
+ git push origin :dev &&
+ test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+test_expect_success 'MKCOL sends directory names with trailing slashes' '
+
+ ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
+x1="[0-9a-f]"
+x2="$x1$x1"
+x5="$x1$x1$x1$x1$x1"
+x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
+x40="$x38$x2"
+
+test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
+ sed \
+ -e "s/PUT /OP /" \
+ -e "s/MOVE /OP /" \
+ -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
+ "$HTTPD_ROOT_PATH"/access.log |
+ grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
+
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$ROOT_PATH"/test_repo_clone master
+
+test_expect_success 'push to password-protected repository (user in URL)' '
+ test_commit pw-user &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'user was prompted only once for password' '
+ expect_askpass pass user@host
+'
+
+test_expect_failure 'push to password-protected repository (no user in URL)' '
+ test_commit pw-nouser &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+ expect_askpass both user@host
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
+stop_httpd
+
+test_done
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
-#
-
-test_description='test WebDAV http-push
-
-This test runs various sanity checks on http-push.'
-
-. ./test-lib.sh
-
-if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
-then
- skip_all="skipping test, USE_CURL_MULTI is not defined"
- test_done
-fi
-
-LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-ROOT_PATH="$PWD"
-start_httpd
-
-test_expect_success 'setup remote repository' '
- cd "$ROOT_PATH" &&
- mkdir test_repo &&
- cd test_repo &&
- git init &&
- : >path1 &&
- git add path1 &&
- test_tick &&
- git commit -m initial &&
- cd - &&
- git clone --bare test_repo test_repo.git &&
- cd test_repo.git &&
- git --bare update-server-info &&
- mv hooks/post-update.sample hooks/post-update &&
- ORIG_HEAD=$(git rev-parse --verify HEAD) &&
- cd - &&
- mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
-'
-
-test_expect_success 'create password-protected repository' '
- mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
- cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
- "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
-'
-
-setup_askpass_helper
-
-test_expect_success 'clone remote repository' '
- cd "$ROOT_PATH" &&
- git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
-'
-
-test_expect_success 'push to remote repository with packed refs' '
- cd "$ROOT_PATH"/test_repo_clone &&
- : >path2 &&
- git add path2 &&
- test_tick &&
- git commit -m path2 &&
- HEAD=$(git rev-parse --verify HEAD) &&
- git push &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push already up-to-date' '
- git push
-'
-
-test_expect_success 'push to remote repository with unpacked refs' '
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- rm packed-refs &&
- git update-ref refs/heads/master $ORIG_HEAD &&
- git --bare update-server-info) &&
- git push &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'http-push fetches unpacked objects' '
- cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
- "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
-
- git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
- "$ROOT_PATH"/fetch_unpacked &&
-
- # By reset, we force git to retrieve the object
- (cd "$ROOT_PATH"/fetch_unpacked &&
- git reset --hard HEAD^ &&
- git remote rm origin &&
- git reflog expire --expire=0 --all &&
- git prune &&
- git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
-'
-
-test_expect_success 'http-push fetches packed objects' '
- cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
- "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
-
- git clone $HTTPD_URL/dumb/test_repo_packed.git \
- "$ROOT_PATH"/test_repo_clone_packed &&
-
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
- git --bare repack &&
- git --bare prune-packed) &&
-
- # By reset, we force git to retrieve the packed object
- (cd "$ROOT_PATH"/test_repo_clone_packed &&
- git reset --hard HEAD^ &&
- git remote remove origin &&
- git reflog expire --expire=0 --all &&
- git prune &&
- git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
-'
-
-test_expect_success 'create and delete remote branch' '
- cd "$ROOT_PATH"/test_repo_clone &&
- git checkout -b dev &&
- : >path3 &&
- git add path3 &&
- test_tick &&
- git commit -m dev &&
- git push origin dev &&
- git push origin :dev &&
- test_must_fail git show-ref --verify refs/remotes/origin/dev
-'
-
-test_expect_success 'MKCOL sends directory names with trailing slashes' '
-
- ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
-
-'
-
-x1="[0-9a-f]"
-x2="$x1$x1"
-x5="$x1$x1$x1$x1$x1"
-x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
-x40="$x38$x2"
-
-test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
- sed \
- -e "s/PUT /OP /" \
- -e "s/MOVE /OP /" \
- -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
- "$HTTPD_ROOT_PATH"/access.log |
- grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
-
-'
-
-test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
- "$ROOT_PATH"/test_repo_clone master
-
-test_expect_success 'push to password-protected repository (user in URL)' '
- test_commit pw-user &&
- set_askpass user@host pass@host &&
- git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
- git rev-parse --verify HEAD >expect &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
- rev-parse --verify HEAD >actual &&
- test_cmp expect actual
-'
-
-test_expect_failure 'user was prompted only once for password' '
- expect_askpass pass user@host
-'
-
-test_expect_failure 'push to password-protected repository (no user in URL)' '
- test_commit pw-nouser &&
- set_askpass user@host pass@host &&
- git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
- expect_askpass both user@host
- git rev-parse --verify HEAD >expect &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
- rev-parse --verify HEAD >actual &&
- test_cmp expect actual
-'
-
-stop_httpd
-
-test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+ cd "$ROOT_PATH" &&
+ mkdir test_repo &&
+ cd test_repo &&
+ git init &&
+ : >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -m initial &&
+ cd - &&
+ git clone --bare test_repo test_repo.git &&
+ cd test_repo.git &&
+ git config http.receivepack true &&
+ git config core.logallrefupdates true &&
+ ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+ cd - &&
+ mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+ # In the URL, add a trailing slash, and see if git appends yet another
+ # slash.
+ cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+ # Clear the log, so that it does not affect the "used receive-pack
+ # service" test which reads the log too.
+ #
+ # We do this before the actual comparison to ensure the log is cleared.
+ echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+ test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+ rm -rf test_repo_clone &&
+ git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
+ (
+ cd test_repo_clone && git config push.default matching
+ )
+'
+
+test_expect_success 'push to remote repository (standard)' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -m path2 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
+ ! grep "Expect: 100-continue" err &&
+ grep "POST git-receive-pack ([0-9]* bytes)" err &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+ git push
+'
+
+test_expect_success 'create and delete remote branch' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev &&
+ : >path3 &&
+ git add path3 &&
+ test_tick &&
+ git commit -m dev &&
+ git push origin dev &&
+ git push origin :dev &&
+ 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:$LIB_HTTPD_PORT/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-upload-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
+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
+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 "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+ test_cmp exp act
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$ROOT_PATH"/test_repo_clone master success
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
+ # create a dissimilarly-named remote ref so that git is unable to match the
+ # two refs (viz. local, remote) unless an explicit refspec is provided.
+ git push origin master:retsam
+
+ echo "change changed" > path2 &&
+ git commit -a -m path2 --amend &&
+
+ # push master too; this ensures there is at least one '"'push'"' command to
+ # the remote helper and triggers interaction with the helper.
+ test_must_fail git push -v origin +master master:retsam >output 2>&1'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
+ grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
+ grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
+'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
+ test_i18ngrep "Updates were rejected because" \
+ output
+'
+
+test_expect_success 'push (chunked)' '
+ git checkout master &&
+ test_commit commit path3 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ test_config http.postbuffer 4 &&
+ git push -v -v origin $BRANCH 2>err &&
+ grep "POST git-receive-pack (chunked)" err &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push --all can push to empty repo' '
+ d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
+ git init --bare "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git push --all "$HTTPD_URL"/smart/empty-all.git
+'
+
+test_expect_success 'push --mirror can push to empty repo' '
+ d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
+ git init --bare "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
+'
+
+test_expect_success 'push --all to repo with alternates' '
+ s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+ d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
+ git clone --bare --shared "$s" "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git --git-dir="$d" repack -adl &&
+ git push --all "$HTTPD_URL"/smart/alternates-all.git
+'
+
+test_expect_success 'push --mirror to repo with alternates' '
+ s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+ d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
+ git clone --bare --shared "$s" "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git --git-dir="$d" repack -adl &&
+ git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
+'
+
+test_expect_success TTY 'push shows progress when stderr is a tty' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit noisy &&
+ test_terminal git push >output 2>&1 &&
+ grep "^Writing objects" output
+'
+
+test_expect_success TTY 'push --quiet silences status and progress' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit quiet &&
+ test_terminal git push --quiet >output 2>&1 &&
+ test_cmp /dev/null output
+'
+
+test_expect_success TTY 'push --no-progress silences progress but not status' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit no-progress &&
+ test_terminal git push --no-progress >output 2>&1 &&
+ grep "^To http" output &&
+ ! grep "^Writing objects"
+'
+
+test_expect_success 'push --progress shows progress to non-tty' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit progress &&
+ git push --progress >output 2>&1 &&
+ grep "^To http" output &&
+ grep "^Writing objects" output
+'
+
+test_expect_success 'http push gives sane defaults to reflog' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit reflog-test &&
+ git push "$HTTPD_URL"/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -g -1 --format="%gn <%ge>" >actual &&
+ echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit custom-reflog-test &&
+ git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -g -1 --format="%gn <%ge>" >actual &&
+ echo "Custom User <custom@example.com>" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push over smart http with auth' '
+ cd "$ROOT_PATH/test_repo_clone" &&
+ echo push-auth-test >expect &&
+ test_commit push-auth-test &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL"/auth/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to auth-only-for-push repo' '
+ cd "$ROOT_PATH/test_repo_clone" &&
+ echo push-half-auth >expect &&
+ test_commit push-half-auth &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create repo without http.receivepack set' '
+ cd "$ROOT_PATH" &&
+ git init half-auth &&
+ (
+ cd half-auth &&
+ test_commit one
+ ) &&
+ git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
+'
+
+test_expect_success 'clone via half-auth-complete does not need password' '
+ cd "$ROOT_PATH" &&
+ set_askpass wrong &&
+ git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
+ half-auth-clone &&
+ expect_askpass none
+'
+
+test_expect_success 'push into half-auth-complete requires password' '
+ cd "$ROOT_PATH/half-auth-clone" &&
+ echo two >expect &&
+ test_commit two &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+stop_httpd
+test_done
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
-#
-
-test_description='test smart pushing over http via http-backend'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
- skip_all='skipping test, git built without http support'
- test_done
-fi
-
-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' '
- cd "$ROOT_PATH" &&
- mkdir test_repo &&
- cd test_repo &&
- git init &&
- : >path1 &&
- git add path1 &&
- test_tick &&
- git commit -m initial &&
- cd - &&
- git clone --bare test_repo test_repo.git &&
- cd test_repo.git &&
- git config http.receivepack true &&
- git config core.logallrefupdates true &&
- ORIG_HEAD=$(git rev-parse --verify HEAD) &&
- cd - &&
- mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
-'
-
-setup_askpass_helper
-
-cat >exp <<EOF
-GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
-EOF
-test_expect_success 'no empty path components' '
- # In the URL, add a trailing slash, and see if git appends yet another
- # slash.
- cd "$ROOT_PATH" &&
- git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
-
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
-
- # Clear the log, so that it does not affect the "used receive-pack
- # service" test which reads the log too.
- #
- # We do this before the actual comparison to ensure the log is cleared.
- echo > "$HTTPD_ROOT_PATH"/access.log &&
-
- test_cmp exp act
-'
-
-test_expect_success 'clone remote repository' '
- rm -rf test_repo_clone &&
- git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
- (
- cd test_repo_clone && git config push.default matching
- )
-'
-
-test_expect_success 'push to remote repository (standard)' '
- cd "$ROOT_PATH"/test_repo_clone &&
- : >path2 &&
- git add path2 &&
- test_tick &&
- git commit -m path2 &&
- HEAD=$(git rev-parse --verify HEAD) &&
- GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
- ! grep "Expect: 100-continue" err &&
- grep "POST git-receive-pack ([0-9]* bytes)" err &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push already up-to-date' '
- git push
-'
-
-test_expect_success 'create and delete remote branch' '
- cd "$ROOT_PATH"/test_repo_clone &&
- git checkout -b dev &&
- : >path3 &&
- git add path3 &&
- test_tick &&
- git commit -m dev &&
- git push origin dev &&
- git push origin :dev &&
- 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:$LIB_HTTPD_PORT/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-upload-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
-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
-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 "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
- test_cmp exp act
-'
-
-test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
- "$ROOT_PATH"/test_repo_clone master success
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
- # create a dissimilarly-named remote ref so that git is unable to match the
- # two refs (viz. local, remote) unless an explicit refspec is provided.
- git push origin master:retsam
-
- echo "change changed" > path2 &&
- git commit -a -m path2 --amend &&
-
- # push master too; this ensures there is at least one '"'push'"' command to
- # the remote helper and triggers interaction with the helper.
- test_must_fail git push -v origin +master master:retsam >output 2>&1'
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
- grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
- grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
-'
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
- test_i18ngrep "Updates were rejected because" \
- output
-'
-
-test_expect_success 'push (chunked)' '
- git checkout master &&
- test_commit commit path3 &&
- HEAD=$(git rev-parse --verify HEAD) &&
- test_config http.postbuffer 4 &&
- git push -v -v origin $BRANCH 2>err &&
- grep "POST git-receive-pack (chunked)" err &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push --all can push to empty repo' '
- d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
- git init --bare "$d" &&
- git --git-dir="$d" config http.receivepack true &&
- git push --all "$HTTPD_URL"/smart/empty-all.git
-'
-
-test_expect_success 'push --mirror can push to empty repo' '
- d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
- git init --bare "$d" &&
- git --git-dir="$d" config http.receivepack true &&
- git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
-'
-
-test_expect_success 'push --all to repo with alternates' '
- s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
- d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
- git clone --bare --shared "$s" "$d" &&
- git --git-dir="$d" config http.receivepack true &&
- git --git-dir="$d" repack -adl &&
- git push --all "$HTTPD_URL"/smart/alternates-all.git
-'
-
-test_expect_success 'push --mirror to repo with alternates' '
- s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
- d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
- git clone --bare --shared "$s" "$d" &&
- git --git-dir="$d" config http.receivepack true &&
- git --git-dir="$d" repack -adl &&
- git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
-'
-
-test_expect_success TTY 'push shows progress when stderr is a tty' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit noisy &&
- test_terminal git push >output 2>&1 &&
- grep "^Writing objects" output
-'
-
-test_expect_success TTY 'push --quiet silences status and progress' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit quiet &&
- test_terminal git push --quiet >output 2>&1 &&
- test_cmp /dev/null output
-'
-
-test_expect_success TTY 'push --no-progress silences progress but not status' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit no-progress &&
- test_terminal git push --no-progress >output 2>&1 &&
- grep "^To http" output &&
- ! grep "^Writing objects"
-'
-
-test_expect_success 'push --progress shows progress to non-tty' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit progress &&
- git push --progress >output 2>&1 &&
- grep "^To http" output &&
- grep "^Writing objects" output
-'
-
-test_expect_success 'http push gives sane defaults to reflog' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit reflog-test &&
- git push "$HTTPD_URL"/smart/test_repo.git &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
- log -g -1 --format="%gn <%ge>" >actual &&
- echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
- test_cmp expect actual
-'
-
-test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
- cd "$ROOT_PATH"/test_repo_clone &&
- test_commit custom-reflog-test &&
- git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
- log -g -1 --format="%gn <%ge>" >actual &&
- echo "Custom User <custom@example.com>" >expect &&
- test_cmp expect actual
-'
-
-test_expect_success 'push over smart http with auth' '
- cd "$ROOT_PATH/test_repo_clone" &&
- echo push-auth-test >expect &&
- test_commit push-auth-test &&
- set_askpass user@host pass@host &&
- git push "$HTTPD_URL"/auth/smart/test_repo.git &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
- log -1 --format=%s >actual &&
- expect_askpass both user@host &&
- test_cmp expect actual
-'
-
-test_expect_success 'push to auth-only-for-push repo' '
- cd "$ROOT_PATH/test_repo_clone" &&
- echo push-half-auth >expect &&
- test_commit push-half-auth &&
- set_askpass user@host pass@host &&
- git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
- log -1 --format=%s >actual &&
- expect_askpass both user@host &&
- test_cmp expect actual
-'
-
-test_expect_success 'create repo without http.receivepack set' '
- cd "$ROOT_PATH" &&
- git init half-auth &&
- (
- cd half-auth &&
- test_commit one
- ) &&
- git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
-'
-
-test_expect_success 'clone via half-auth-complete does not need password' '
- cd "$ROOT_PATH" &&
- set_askpass wrong &&
- git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
- half-auth-clone &&
- expect_askpass none
-'
-
-test_expect_success 'push into half-auth-complete requires password' '
- cd "$ROOT_PATH/half-auth-clone" &&
- echo two >expect &&
- test_commit two &&
- set_askpass user@host pass@host &&
- git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
- log -1 --format=%s >actual &&
- expect_askpass both user@host &&
- test_cmp expect actual
-'
-
-stop_httpd
-test_done
--- /dev/null
+#!/bin/sh
+
+test_description='test dumb fetching over http via static file'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+ git config push.default matching &&
+ echo content1 >file &&
+ git add file &&
+ git commit -m one
+ echo content2 >file &&
+ git add file &&
+ git commit -m two
+'
+
+test_expect_success 'create http-accessible bare repository with loose objects' '
+ cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git config core.bare true &&
+ mkdir -p hooks &&
+ echo "exec git update-server-info" >hooks/post-update &&
+ chmod +x hooks/post-update &&
+ hooks/post-update
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+ git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
+ cp -R clone-tmpl clone &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'create password-protected repository' '
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
+ cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
+'
+
+setup_askpass_helper
+
+test_expect_success 'cloning password-protected repository can fail' '
+ set_askpass wrong &&
+ test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
+ expect_askpass both wrong
+'
+
+test_expect_success 'http auth can use user/pass in URL' '
+ set_askpass wrong &&
+ git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth can use just user in URL' '
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'http auth can request both user and pass' '
+ set_askpass user@host pass@host &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
+ expect_askpass both user@host
+'
+
+test_expect_success 'http auth respects credential helper config' '
+ test_config_global credential.helper "!f() {
+ cat >/dev/null
+ echo username=user@host
+ echo password=pass@host
+ }; f" &&
+ set_askpass wrong &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth can get username from config' '
+ test_config_global "credential.$HTTPD_URL.username" user@host &&
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'configured username does not override URL' '
+ test_config_global "credential.$HTTPD_URL.username" wrong &&
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'fetch changes via http' '
+ echo content >>file &&
+ git commit -a -m two &&
+ git push public &&
+ (cd clone && git pull) &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via manual http-fetch' '
+ cp -R clone-tmpl clone2 &&
+
+ HEAD=$(git rev-parse --verify HEAD) &&
+ (cd clone2 &&
+ git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
+ git checkout master-new &&
+ test $HEAD = $(git rev-parse --verify HEAD)) &&
+ test_cmp file clone2/file
+'
+
+test_expect_success 'http remote detects correct HEAD' '
+ git push public master:other &&
+ (cd clone &&
+ git remote set-head origin -d &&
+ git remote set-head origin -a &&
+ git symbolic-ref refs/remotes/origin/HEAD > output &&
+ echo refs/remotes/origin/master > expect &&
+ test_cmp expect output
+ )
+'
+
+test_expect_success 'fetch packed objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ git --bare repack -a -d
+ ) &&
+ git clone $HTTPD_URL/dumb/repo_pack.git
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ p=`ls objects/pack/pack-*.pack` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad1.git &&
+ (cd repo_bad1.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+ test 0 = `ls objects/pack/pack-*.pack | wc -l`
+ )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ p=`ls objects/pack/pack-*.idx` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad2.git &&
+ (cd repo_bad2.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+ test 0 = `ls objects/pack | wc -l`
+ )
+'
+
+test_expect_success 'did not use upload-pack service' '
+ grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+ : >exp
+ test_cmp exp act
+'
+
+stop_httpd
+test_done
+++ /dev/null
-#!/bin/sh
-
-test_description='test dumb fetching over http via static file'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
- skip_all='skipping test, git built without http support'
- test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'setup repository' '
- git config push.default matching &&
- echo content1 >file &&
- git add file &&
- git commit -m one
- echo content2 >file &&
- git add file &&
- git commit -m two
-'
-
-test_expect_success 'create http-accessible bare repository with loose objects' '
- cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git config core.bare true &&
- mkdir -p hooks &&
- echo "exec git update-server-info" >hooks/post-update &&
- chmod +x hooks/post-update &&
- hooks/post-update
- ) &&
- git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git push public master:master
-'
-
-test_expect_success 'clone http repository' '
- git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
- cp -R clone-tmpl clone &&
- test_cmp file clone/file
-'
-
-test_expect_success 'create password-protected repository' '
- mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
- cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
- "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
-'
-
-setup_askpass_helper
-
-test_expect_success 'cloning password-protected repository can fail' '
- set_askpass wrong &&
- test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
- expect_askpass both wrong
-'
-
-test_expect_success 'http auth can use user/pass in URL' '
- set_askpass wrong &&
- git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
- expect_askpass none
-'
-
-test_expect_success 'http auth can use just user in URL' '
- set_askpass wrong pass@host &&
- git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
- expect_askpass pass user@host
-'
-
-test_expect_success 'http auth can request both user and pass' '
- set_askpass user@host pass@host &&
- git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
- expect_askpass both user@host
-'
-
-test_expect_success 'http auth respects credential helper config' '
- test_config_global credential.helper "!f() {
- cat >/dev/null
- echo username=user@host
- echo password=pass@host
- }; f" &&
- set_askpass wrong &&
- git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
- expect_askpass none
-'
-
-test_expect_success 'http auth can get username from config' '
- test_config_global "credential.$HTTPD_URL.username" user@host &&
- set_askpass wrong pass@host &&
- git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
- expect_askpass pass user@host
-'
-
-test_expect_success 'configured username does not override URL' '
- test_config_global "credential.$HTTPD_URL.username" wrong &&
- set_askpass wrong pass@host &&
- git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
- expect_askpass pass user@host
-'
-
-test_expect_success 'fetch changes via http' '
- echo content >>file &&
- git commit -a -m two &&
- git push public &&
- (cd clone && git pull) &&
- test_cmp file clone/file
-'
-
-test_expect_success 'fetch changes via manual http-fetch' '
- cp -R clone-tmpl clone2 &&
-
- HEAD=$(git rev-parse --verify HEAD) &&
- (cd clone2 &&
- git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
- git checkout master-new &&
- test $HEAD = $(git rev-parse --verify HEAD)) &&
- test_cmp file clone2/file
-'
-
-test_expect_success 'http remote detects correct HEAD' '
- git push public master:other &&
- (cd clone &&
- git remote set-head origin -d &&
- git remote set-head origin -a &&
- git symbolic-ref refs/remotes/origin/HEAD > output &&
- echo refs/remotes/origin/master > expect &&
- test_cmp expect output
- )
-'
-
-test_expect_success 'fetch packed objects' '
- cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
- git --bare repack -a -d
- ) &&
- git clone $HTTPD_URL/dumb/repo_pack.git
-'
-
-test_expect_success 'fetch notices corrupt pack' '
- cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
- p=`ls objects/pack/pack-*.pack` &&
- chmod u+w $p &&
- printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
- ) &&
- mkdir repo_bad1.git &&
- (cd repo_bad1.git &&
- git --bare init &&
- test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
- test 0 = `ls objects/pack/pack-*.pack | wc -l`
- )
-'
-
-test_expect_success 'fetch notices corrupt idx' '
- cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
- p=`ls objects/pack/pack-*.idx` &&
- chmod u+w $p &&
- printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
- ) &&
- mkdir repo_bad2.git &&
- (cd repo_bad2.git &&
- git --bare init &&
- test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
- test 0 = `ls objects/pack | wc -l`
- )
-'
-
-test_expect_success 'did not use upload-pack service' '
- grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
- : >exp
- test_cmp exp act
-'
-
-stop_httpd
-test_done
--- /dev/null
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+ git config push.default matching &&
+ echo content >file &&
+ git add file &&
+ git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git --bare init
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Accept-Encoding: gzip
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: gzip
+> Content-Type: application/x-git-upload-pack-request
+> Accept: application/x-git-upload-pack-result
+> Content-Length: xxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+EOF
+test_expect_success 'clone http repository' '
+ GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+ test_cmp file clone/file &&
+ tr '\''\015'\'' Q <err |
+ sed -e "
+ s/Q\$//
+ /^[*] /d
+ /^$/d
+ /^< $/d
+
+ /^[^><]/{
+ s/^/> /
+ }
+
+ /^> User-Agent: /d
+ /^> Host: /d
+ /^> POST /,$ {
+ /^> Accept: [*]\\/[*]/d
+ }
+ s/^> Content-Length: .*/> Content-Length: xxx/
+ /^> 00..want /d
+ /^> 00.*done/d
+
+ /^< Server: /d
+ /^< Expires: /d
+ /^< Date: /d
+ /^< Content-Length: /d
+ /^< Transfer-Encoding: /d
+ " >act &&
+ test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+ echo content >>file &&
+ git commit -a -m two &&
+ git push public
+ (cd clone && git pull) &&
+ test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+ test_cmp exp act
+'
+
+test_expect_success 'follow redirects (301)' '
+ git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
+'
+
+test_expect_success 'follow redirects (302)' '
+ git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
+'
+
+test_expect_success 'redirects re-root further requests' '
+ git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
+'
+
+test_expect_success 'clone from password-protected repository' '
+ echo two >expect &&
+ set_askpass user@host pass@host &&
+ git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
+ expect_askpass both user@host &&
+ git --git-dir=smart-auth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-push repository' '
+ echo two >expect &&
+ set_askpass wrong &&
+ git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
+ expect_askpass none &&
+ git --git-dir=smart-noauth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-objects repository' '
+ echo two >expect &&
+ set_askpass user@host pass@host &&
+ git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
+ expect_askpass both user@host &&
+ git --git-dir=half-auth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-op half-auth fetch does not require a password' '
+ set_askpass wrong &&
+ git --git-dir=half-auth fetch &&
+ expect_askpass none
+'
+
+test_expect_success 'redirects send auth to new location' '
+ set_askpass user@host pass@host &&
+ git -c credential.useHttpPath=true \
+ clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
+ expect_askpass both user@host auth/smart/repo.git
+'
+
+test_expect_success 'disable dumb http on server' '
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ config http.getanyfile false
+'
+
+test_expect_success 'GIT_SMART_HTTP can disable smart http' '
+ (GIT_SMART_HTTP=0 &&
+ export GIT_SMART_HTTP &&
+ cd clone &&
+ test_must_fail git fetch)
+'
+
+test_expect_success 'invalid Content-Type rejected' '
+ test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual
+ grep "not valid:" actual
+'
+
+test_expect_success 'create namespaced refs' '
+ test_commit namespaced &&
+ git push public HEAD:refs/namespaces/ns/refs/heads/master &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
+'
+
+test_expect_success 'smart clone respects namespace' '
+ git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
+ echo namespaced >expect &&
+ git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'dumb clone via http-backend respects namespace' '
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ config http.getanyfile true &&
+ GIT_SMART_HTTP=0 git clone \
+ "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
+ echo namespaced >expect &&
+ git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+cat >cookies.txt <<EOF
+127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+EOF
+cat >expect_cookies.txt <<EOF
+
+127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
+EOF
+test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+ git config http.cookiefile cookies.txt &&
+ git config http.savecookies true &&
+ git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
+ tail -3 cookies.txt > cookies_tail.txt
+ test_cmp expect_cookies.txt cookies_tail.txt
+'
+
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ for i in `test_seq 50000`
+ do
+ echo "commit refs/heads/too-many-refs"
+ echo "mark :$i"
+ echo "committer git <git@example.com> $i +0000"
+ echo "data 0"
+ echo "M 644 inline bla.txt"
+ echo "data 4"
+ echo "bla"
+ # make every commit dangling by always
+ # rewinding the branch after each commit
+ echo "reset refs/heads/too-many-refs"
+ echo "from :1"
+ done | git fast-import --export-marks=marks &&
+
+ # now assign tags to all the dangling commits we created above
+ tag=$(perl -e "print \"bla\" x 30") &&
+ sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+ )
+'
+
+test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
+ git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
+ test_line_count = 0 err &&
+ (
+ cd too-many-refs &&
+ test $(git for-each-ref refs/tags | wc -l) = 50000
+ )
+'
+
+stop_httpd
+test_done
+++ /dev/null
-#!/bin/sh
-
-test_description='test smart fetching over http via http-backend'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
- skip_all='skipping test, git built without http support'
- test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'setup repository' '
- git config push.default matching &&
- echo content >file &&
- git add file &&
- git commit -m one
-'
-
-test_expect_success 'create http-accessible bare repository' '
- mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git --bare init
- ) &&
- git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git push public master:master
-'
-
-setup_askpass_helper
-
-cat >exp <<EOF
-> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
-> Accept: */*
-> Accept-Encoding: gzip
-> Pragma: no-cache
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-advertisement
-> POST /smart/repo.git/git-upload-pack HTTP/1.1
-> Accept-Encoding: gzip
-> Content-Type: application/x-git-upload-pack-request
-> Accept: application/x-git-upload-pack-result
-> Content-Length: xxx
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-result
-EOF
-test_expect_success 'clone http repository' '
- GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
- test_cmp file clone/file &&
- tr '\''\015'\'' Q <err |
- sed -e "
- s/Q\$//
- /^[*] /d
- /^$/d
- /^< $/d
-
- /^[^><]/{
- s/^/> /
- }
-
- /^> User-Agent: /d
- /^> Host: /d
- /^> POST /,$ {
- /^> Accept: [*]\\/[*]/d
- }
- s/^> Content-Length: .*/> Content-Length: xxx/
- /^> 00..want /d
- /^> 00.*done/d
-
- /^< Server: /d
- /^< Expires: /d
- /^< Date: /d
- /^< Content-Length: /d
- /^< Transfer-Encoding: /d
- " >act &&
- test_cmp exp act
-'
-
-test_expect_success 'fetch changes via http' '
- echo content >>file &&
- git commit -a -m two &&
- git push public
- (cd clone && git pull) &&
- test_cmp file clone/file
-'
-
-cat >exp <<EOF
-GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-EOF
-test_expect_success 'used upload-pack service' '
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
- test_cmp exp act
-'
-
-test_expect_success 'follow redirects (301)' '
- git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
-'
-
-test_expect_success 'follow redirects (302)' '
- git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
-'
-
-test_expect_success 'redirects re-root further requests' '
- git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
-'
-
-test_expect_success 'clone from password-protected repository' '
- echo two >expect &&
- set_askpass user@host pass@host &&
- git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
- expect_askpass both user@host &&
- git --git-dir=smart-auth log -1 --format=%s >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'clone from auth-only-for-push repository' '
- echo two >expect &&
- set_askpass wrong &&
- git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
- expect_askpass none &&
- git --git-dir=smart-noauth log -1 --format=%s >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'clone from auth-only-for-objects repository' '
- echo two >expect &&
- set_askpass user@host pass@host &&
- git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
- expect_askpass both user@host &&
- git --git-dir=half-auth log -1 --format=%s >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'no-op half-auth fetch does not require a password' '
- set_askpass wrong &&
- git --git-dir=half-auth fetch &&
- expect_askpass none
-'
-
-test_expect_success 'redirects send auth to new location' '
- set_askpass user@host pass@host &&
- git -c credential.useHttpPath=true \
- clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
- expect_askpass both user@host auth/smart/repo.git
-'
-
-test_expect_success 'disable dumb http on server' '
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
- config http.getanyfile false
-'
-
-test_expect_success 'GIT_SMART_HTTP can disable smart http' '
- (GIT_SMART_HTTP=0 &&
- export GIT_SMART_HTTP &&
- cd clone &&
- test_must_fail git fetch)
-'
-
-test_expect_success 'invalid Content-Type rejected' '
- test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual
- grep "not valid:" actual
-'
-
-test_expect_success 'create namespaced refs' '
- test_commit namespaced &&
- git push public HEAD:refs/namespaces/ns/refs/heads/master &&
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
- symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
-'
-
-test_expect_success 'smart clone respects namespace' '
- git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
- echo namespaced >expect &&
- git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'dumb clone via http-backend respects namespace' '
- git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
- config http.getanyfile true &&
- GIT_SMART_HTTP=0 git clone \
- "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
- echo namespaced >expect &&
- git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
- test_cmp expect actual
-'
-
-cat >cookies.txt <<EOF
-127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
-EOF
-cat >expect_cookies.txt <<EOF
-
-127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
-127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
-EOF
-test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
- git config http.cookiefile cookies.txt &&
- git config http.savecookies true &&
- git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
- tail -3 cookies.txt > cookies_tail.txt
- test_cmp expect_cookies.txt cookies_tail.txt
-'
-
-test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
-
-test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
- (
- cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- for i in `test_seq 50000`
- do
- echo "commit refs/heads/too-many-refs"
- echo "mark :$i"
- echo "committer git <git@example.com> $i +0000"
- echo "data 0"
- echo "M 644 inline bla.txt"
- echo "data 4"
- echo "bla"
- # make every commit dangling by always
- # rewinding the branch after each commit
- echo "reset refs/heads/too-many-refs"
- echo "from :1"
- done | git fast-import --export-marks=marks &&
-
- # now assign tags to all the dangling commits we created above
- tag=$(perl -e "print \"bla\" x 30") &&
- sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
- )
-'
-
-test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
- git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
- test_line_count = 0 err &&
- (
- cd too-many-refs &&
- test $(git for-each-ref refs/tags | wc -l) = 50000
- )
-'
-
-stop_httpd
-test_done
test_done
fi
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5561'}
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
test_cmp expect actual
'
-test_expect_success 'match_pathspec_depth matches :(icase)bar' '
+test_expect_success 'match_pathspec matches :(icase)bar' '
cat <<-EOF >expect &&
BAR
bAr
test_cmp expect actual
'
-test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' '
+test_expect_success 'match_pathspec matches :(icase)bar with prefix' '
cat <<-EOF >expect &&
fOo/BAR
fOo/bAr
test_cmp expect actual
'
-test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' '
+test_expect_success 'match_pathspec matches :(icase)bar with empty prefix' '
cat <<-EOF >expect &&
bar
fOo/BAR
git submodule add ./. sub &&
echo content >file &&
git add file &&
- git commit -m "added sub and file"
+ git commit -m "added sub and file" &&
+ git branch submodule
'
test_expect_success 'git mv cannot move a submodule in a file' '
! test -s actual
'
+test_expect_success 'mv -k does not accidentally destroy submodules' '
+ git checkout submodule &&
+ mkdir dummy dest &&
+ git mv -k dummy sub dest &&
+ git status --porcelain >actual &&
+ grep "^R sub -> dest/sub" actual &&
+ git reset --hard &&
+ git checkout .
+'
+
test_done
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- deleted by us: foo
+ deleted by us: foo
no changes added to commit (use "git add" and/or "git commit -a")
EOF
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- both added: conflict.txt
- deleted by them: main.txt
+ both added: conflict.txt
+ deleted by them: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- both deleted: main.txt
- added by them: sub_master.txt
- added by us: sub_second.txt
+ both deleted: main.txt
+ added by them: sub_master.txt
+ added by us: sub_second.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
Unmerged paths:
(use "git rm <file>..." to mark resolution)
- both deleted: main.txt
+ both deleted: main.txt
Untracked files not listed (use -u option to show untracked files)
EOF
git diff HEAD --exit-code
'
+test_expect_success 'reset --mixed sets up work tree' '
+ git init mixed_worktree &&
+ (
+ cd mixed_worktree &&
+ test_commit dummy
+ ) &&
+ : >expect &&
+ git --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual &&
+ test_cmp expect actual
+'
+
test_done
! test -d foo
'
+test_expect_success 'git clean -d respects pathspecs (dir is prefix of pathspec)' '
+ mkdir -p foo &&
+ mkdir -p foobar &&
+ git clean -df foobar &&
+ test_path_is_dir foo &&
+ test_path_is_missing foobar
+'
+
+test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir)' '
+ mkdir -p foo &&
+ mkdir -p foobar &&
+ git clean -df foo &&
+ test_path_is_missing foo &&
+ test_path_is_dir foobar
+'
+
test_done
test_expect_success 'with hook (merge)' '
- head=`git rev-parse HEAD` &&
- git checkout -b other HEAD@{1} &&
- echo "more" >> file &&
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
+ git add file &&
+ git commit -m other &&
+ git checkout - &&
+ git merge --no-ff other &&
+ test "`git log -1 --pretty=format:%s`" = "merge (no editor)"
+'
+
+test_expect_success 'with hook and editor (merge)' '
+
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
git add file &&
git commit -m other &&
git checkout - &&
- git merge other &&
- test "`git log -1 --pretty=format:%s`" = merge
+ env GIT_EDITOR="\"\$FAKE_EDITOR\"" git merge --no-ff -e other &&
+ test "`git log -1 --pretty=format:%s`" = "merge"
'
cat > "$HOOK" <<'EOF'
test_expect_success 'with failing hook' '
+ test_when_finished "git checkout -f master" &&
head=`git rev-parse HEAD` &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
'
test_expect_success 'with failing hook (--no-verify)' '
+ test_when_finished "git checkout -f master" &&
head=`git rev-parse HEAD` &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
'
test_expect_success 'with failing hook (merge)' '
+ test_when_finished "git checkout -f master" &&
git checkout -B other HEAD@{1} &&
echo "more" >> file &&
git add file &&
rm -f "$HOOK" &&
git commit -m other &&
- write_script "$HOOK" <<-EOF
+ write_script "$HOOK" <<-EOF &&
exit 1
EOF
git checkout - &&
- test_must_fail git merge other
+ test_must_fail git merge --no-ff other
'
Unmerged paths:
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
Unmerged paths:
- both modified: main.txt
+ both modified: main.txt
no changes added to commit
EOF
Unmerged paths:
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: to-revert.txt
+ both modified: to-revert.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
--- /dev/null
+#!/bin/sh
+
+test_description='hunk edit with "commit -p -m"'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL
+then
+ skip_all="skipping '$test_description' tests, perl not available"
+ test_done
+fi
+
+test_expect_success 'setup (initial)' '
+ echo line1 >file &&
+ git add file &&
+ git commit -m commit1
+'
+
+test_expect_success 'edit hunk "commit -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit2 file &&
+ test -r editor_was_started
+'
+
+test_expect_success 'edit hunk "commit --dry-run -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit3 file &&
+ test -r editor_was_started
+'
+
+test_done
)
'
+test_expect_success PERL 'difftool properly honors gitlink and core.worktree' '
+ git submodule add ./. submod/ule &&
+ (
+ cd submod/ule &&
+ test_config diff.tool checktrees &&
+ test_config difftool.checktrees.cmd '\''
+ test -d "$LOCAL" && test -d "$REMOTE" && echo good
+ '\'' &&
+ echo good >expect &&
+ git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
if test "$help" = "t"
then
- echo "$test_description"
+ printf '%s\n' "$test_description"
exit 0
fi
test_failure=$(($test_failure + 1))
say_color error "not ok $test_count - $1"
shift
- echo "$@" | sed -e 's/^/# /'
+ printf '%s\n' "$*" | sed -e 's/^/# /'
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
}
const char *argv[12];
int i, arg = 0;
FILE *pipe_fd;
- char *shallow_file = NULL;
+ const char *shallow_file = NULL;
if (shallow_nr) {
shallow_file = setup_temporary_shallow(NULL);
error("git upload-pack: git-pack-objects died with error.");
goto fail;
}
- if (shallow_file) {
- if (*shallow_file)
- unlink(shallow_file);
- free(shallow_file);
- }
/* flush the data */
if (0 <= buffered) {
#define quote_path quote_path_relative
-static void wt_status_print_unmerged_data(struct wt_status *s,
- struct string_list_item *it)
+static const char *wt_status_unmerged_status_string(int stagemask)
{
- const char *c = color(WT_STATUS_UNMERGED, s);
- struct wt_status_change_data *d = it->util;
- struct strbuf onebuf = STRBUF_INIT;
- const char *one, *how = _("bug");
-
- one = quote_path(it->string, s->prefix, &onebuf);
- status_printf(s, color(WT_STATUS_HEADER, s), "\t");
- switch (d->stagemask) {
- case 1: how = _("both deleted:"); break;
- case 2: how = _("added by us:"); break;
- case 3: how = _("deleted by them:"); break;
- case 4: how = _("added by them:"); break;
- case 5: how = _("deleted by us:"); break;
- case 6: how = _("both added:"); break;
- case 7: how = _("both modified:"); break;
+ switch (stagemask) {
+ case 1:
+ return _("both deleted:");
+ case 2:
+ return _("added by us:");
+ case 3:
+ return _("deleted by them:");
+ case 4:
+ return _("added by them:");
+ case 5:
+ return _("deleted by us:");
+ case 6:
+ return _("both added:");
+ case 7:
+ return _("both modified:");
+ default:
+ die(_("bug: unhandled unmerged status %x"), stagemask);
}
- status_printf_more(s, c, "%-20s%s\n", how, one);
- strbuf_release(&onebuf);
}
static const char *wt_status_diff_status_string(int status)
{
switch (status) {
case DIFF_STATUS_ADDED:
- return _("new file");
+ return _("new file:");
case DIFF_STATUS_COPIED:
- return _("copied");
+ return _("copied:");
case DIFF_STATUS_DELETED:
- return _("deleted");
+ return _("deleted:");
case DIFF_STATUS_MODIFIED:
- return _("modified");
+ return _("modified:");
case DIFF_STATUS_RENAMED:
- return _("renamed");
+ return _("renamed:");
case DIFF_STATUS_TYPE_CHANGED:
- return _("typechange");
+ return _("typechange:");
case DIFF_STATUS_UNKNOWN:
- return _("unknown");
+ return _("unknown:");
case DIFF_STATUS_UNMERGED:
- return _("unmerged");
+ return _("unmerged:");
default:
return NULL;
}
}
+static int maxwidth(const char *(*label)(int), int minval, int maxval)
+{
+ int result = 0, i;
+
+ for (i = minval; i <= maxval; i++) {
+ const char *s = label(i);
+ int len = s ? utf8_strwidth(s) : 0;
+ if (len > result)
+ result = len;
+ }
+ return result;
+}
+
+static void wt_status_print_unmerged_data(struct wt_status *s,
+ struct string_list_item *it)
+{
+ const char *c = color(WT_STATUS_UNMERGED, s);
+ struct wt_status_change_data *d = it->util;
+ struct strbuf onebuf = STRBUF_INIT;
+ static char *padding;
+ static int label_width;
+ const char *one, *how;
+ int len;
+
+ if (!padding) {
+ label_width = maxwidth(wt_status_unmerged_status_string, 1, 7);
+ label_width += strlen(" ");
+ padding = xmallocz(label_width);
+ memset(padding, ' ', label_width);
+ }
+
+ one = quote_path(it->string, s->prefix, &onebuf);
+ status_printf(s, color(WT_STATUS_HEADER, s), "\t");
+
+ how = wt_status_unmerged_status_string(d->stagemask);
+ len = label_width - utf8_strwidth(how);
+ status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one);
+ strbuf_release(&onebuf);
+}
+
static void wt_status_print_change_data(struct wt_status *s,
int change_type,
struct string_list_item *it)
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
struct strbuf extra = STRBUF_INIT;
static char *padding;
+ static int label_width;
const char *what;
int len;
if (!padding) {
- int width = 0;
- /* If DIFF_STATUS_* uses outside this range, we're in trouble */
- for (status = 'A'; status <= 'Z'; status++) {
- what = wt_status_diff_status_string(status);
- len = what ? strlen(what) : 0;
- if (len > width)
- width = len;
- }
- width += 2; /* colon and a space */
- padding = xmallocz(width);
- memset(padding, ' ', width);
+ /* If DIFF_STATUS_* uses outside the range [A..Z], we're in trouble */
+ label_width = maxwidth(wt_status_diff_status_string, 'A', 'Z');
+ label_width += strlen(" ");
+ padding = xmallocz(label_width);
+ memset(padding, ' ', label_width);
}
one_name = two_name = it->string;
what = wt_status_diff_status_string(status);
if (!what)
die(_("bug: unhandled diff status %c"), status);
- /* 1 for colon, which is not part of "what" */
- len = strlen(padding) - (utf8_strwidth(what) + 1);
+ len = label_width - utf8_strwidth(what);
assert(len >= 0);
if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)
- status_printf_more(s, c, "%s:%.*s%s -> %s",
+ status_printf_more(s, c, "%s%.*s%s -> %s",
what, len, padding, one, two);
else
- status_printf_more(s, c, "%s:%.*s%s",
+ status_printf_more(s, c, "%s%.*s%s",
what, len, padding, one);
if (extra.len) {
status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
struct wt_status_change_data *d;
const struct cache_entry *ce = active_cache[i];
- if (!ce_path_match(ce, &s->pathspec))
+ if (!ce_path_match(ce, &s->pathspec, NULL))
continue;
it = string_list_insert(&s->change, ce->name);
d = it->util;
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
if (cache_name_is_other(ent->name, ent->len) &&
- match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
+ dir_path_match(ent, &s->pathspec, 0, NULL))
string_list_insert(&s->untracked, ent->name);
free(ent);
}
for (i = 0; i < dir.ignored_nr; i++) {
struct dir_entry *ent = dir.ignored[i];
if (cache_name_is_other(ent->name, ent->len) &&
- match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
+ dir_path_match(ent, &s->pathspec, 0, NULL))
string_list_insert(&s->ignored, ent->name);
free(ent);
}
return;
}
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
color_fprintf(s->fp, header_color, " [");
if (upstream_is_gone) {
- color_fprintf(s->fp, header_color, _("gone"));
+ color_fprintf(s->fp, header_color, LABEL(N_("gone")));
} else if (!num_ours) {
- color_fprintf(s->fp, header_color, _("behind "));
+ color_fprintf(s->fp, header_color, LABEL(N_("behind ")));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
} else if (!num_theirs) {
- color_fprintf(s->fp, header_color, _("ahead "));
+ color_fprintf(s->fp, header_color, LABEL(N_(("ahead "))));
color_fprintf(s->fp, branch_color_local, "%d", num_ours);
} else {
- color_fprintf(s->fp, header_color, _("ahead "));
+ color_fprintf(s->fp, header_color, LABEL(N_(("ahead "))));
color_fprintf(s->fp, branch_color_local, "%d", num_ours);
- color_fprintf(s->fp, header_color, _(", behind "));
+ color_fprintf(s->fp, header_color, ", %s", LABEL(N_("behind ")));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
}
s->use_color = 0;
s->relative_paths = 0;
s->prefix = NULL;
+ s->no_gettext = 1;
wt_shortstatus_print(s);
}
enum commit_whence whence;
int nowarn;
int use_color;
+ int no_gettext;
int display_comment_prefix;
int relative_paths;
int submodule_summary;