Merge pull request #3333 from libgit2/cmn/for-v23

Maintenance updates for v0.23
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 67444cb..6be1240 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -143,10 +143,10 @@
 	 * network.
 	 */
 	if (stats->local_objects > 0) {
-		printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
+		printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n",
 		       stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
 	} else{
-		printf("\rReceived %d/%d objects in %zu bytes\n",
+		printf("\rReceived %d/%d objects in %" PRIuZ "bytes\n",
 			stats->indexed_objects, stats->total_objects, stats->received_bytes);
 	}
 
diff --git a/include/git2/errors.h b/include/git2/errors.h
index a81aa05..e189e55 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -48,6 +48,7 @@
 	GIT_EEOF            = -20,      /**< Unexpected EOF */
 	GIT_EINVALID        = -21,      /**< Invalid operation or input */
 	GIT_EUNCOMMITTED    = -22,	/**< Uncommitted changes in index prevented operation */
+	GIT_EDIRECTORY      = -23,      /**< The operation is not valid for a directory */
 
 	GIT_PASSTHROUGH     = -30,	/**< Internal only */
 	GIT_ITEROVER        = -31,	/**< Signals end of iteration with iterator */
diff --git a/include/git2/remote.h b/include/git2/remote.h
index e47f008..444fe52 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -511,6 +511,14 @@
 	GIT_REMOTE_DOWNLOAD_TAGS_ALL,
 } git_remote_autotag_option_t;
 
+/**
+ * Fetch options structure.
+ *
+ * Zero out for defaults.  Initialize with `GIT_FETCH_OPTIONS_INIT` macro to
+ * correctly set the `version` field.  E.g.
+ *
+ *		git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
+ */
 typedef struct {
 	int version;
 
@@ -739,7 +747,7 @@
  * stored here for further processing by the caller. Always free this
  * strarray on successful return.
  * @param repo the repository in which to rename
- * @param name the current name of the reamote
+ * @param name the current name of the remote
  * @param new_name the new name the remote should bear
  * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
  */
diff --git a/src/blame_git.c b/src/blame_git.c
index e863efe..f481426 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -304,21 +304,16 @@
 }
 
 static int my_emit(
-		xdfenv_t *xe,
-		xdchange_t *xscr,
-		xdemitcb_t *ecb,
-		xdemitconf_t const *xecfg)
+	long start_a, long count_a,
+	long start_b, long count_b,
+	void *cb_data)
 {
-	xdchange_t *xch = xscr;
-	GIT_UNUSED(xe);
-	GIT_UNUSED(xecfg);
-	while (xch) {
-		blame_chunk_cb_data *d = ecb->priv;
-		blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
-		d->plno = xch->i1 + xch->chg1;
-		d->tlno = xch->i2 + xch->chg2;
-		xch = xch->next;
-	}
+	blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
+
+	blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
+	d->plno = start_a + count_a;
+	d->tlno = start_b + count_b;
+	
 	return 0;
 }
 
@@ -352,7 +347,7 @@
 	xdemitconf_t xecfg = {0};
 	xdemitcb_t ecb = {0};
 
-	xecfg.emit_func = (void(*)(void))my_emit;
+	xecfg.hunk_func = my_emit;
 	ecb.priv = cb_data;
 
 	trim_common_tail(&file_a, &file_b, 0);
diff --git a/src/blob.c b/src/blob.c
index 07c4d92..ad0f4ac 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -185,6 +185,12 @@
 		(error = git_repository_odb(&odb, repo)) < 0)
 		goto done;
 
+	if (S_ISDIR(st.st_mode)) {
+		giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path);
+		error = GIT_EDIRECTORY;
+		goto done;
+	}
+
 	if (out_st)
 		memcpy(out_st, &st, sizeof(st));
 
diff --git a/src/cache.c b/src/cache.c
index 2f3ad15..ca5173c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -50,16 +50,16 @@
 	if (kh_size(cache->map) == 0)
 		return;
 
-	printf("Cache %p: %d items cached, %d bytes\n",
-		cache, kh_size(cache->map), (int)cache->used_memory);
+	printf("Cache %p: %d items cached, %"PRIdZ" bytes\n",
+		cache, kh_size(cache->map), cache->used_memory);
 
 	kh_foreach_value(cache->map, object, {
 		char oid_str[9];
-		printf(" %s%c %s (%d)\n",
+		printf(" %s%c %s (%"PRIuZ")\n",
 			git_object_type2string(object->type),
 			object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
 			git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
-			(int)object->size
+			object->size
 		);
 	});
 }
diff --git a/src/checkout.c b/src/checkout.c
index a94d509..4b3acbc 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1299,8 +1299,8 @@
 	if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
 		(data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
 	{
-		giterr_set(GITERR_CHECKOUT, "%d %s checkout",
-			(int)counts[CHECKOUT_ACTION__CONFLICT],
+		giterr_set(GITERR_CHECKOUT, "%"PRIuZ" %s checkout",
+			counts[CHECKOUT_ACTION__CONFLICT],
 			counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
 			"conflict prevents" : "conflicts prevent");
 		error = GIT_ECONFLICT;
diff --git a/src/filter.c b/src/filter.c
index e25d37c..4006351 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -947,18 +947,20 @@
 {
 	git_vector filter_streams = GIT_VECTOR_INIT;
 	git_writestream *stream_start;
-	int error = 0;
+	int error = 0, close_error;
 
 	git_buf_sanitize(data);
 
-	if ((error = stream_list_init(
-			&stream_start, &filter_streams, filters, target)) == 0 &&
-		(error =
-			stream_start->write(stream_start, data->ptr, data->size)) == 0)
-		error = stream_start->close(stream_start);
+	if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
+		goto out;
 
+	error = stream_start->write(stream_start, data->ptr, data->size);
+
+out:
+	close_error = stream_start->close(stream_start);
 	stream_list_free(&filter_streams);
-	return error;
+	/* propagate the stream init or write error */
+	return error < 0 ? error : close_error;
 }
 
 int git_filter_list_stream_blob(
diff --git a/src/index.c b/src/index.c
index 5ce5522..501498e 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1236,10 +1236,30 @@
 
 	assert(index && path);
 
-	if ((ret = index_entry_init(&entry, index, path)) < 0 ||
-		(ret = index_insert(index, &entry, 1, false)) < 0)
+	if ((ret = index_entry_init(&entry, index, path)) == 0)
+		ret = index_insert(index, &entry, 1, false);
+
+	/* If we were given a directory, let's see if it's a submodule */
+	if (ret < 0 && ret != GIT_EDIRECTORY)
 		return ret;
 
+	if (ret == GIT_EDIRECTORY) {
+		git_submodule *sm;
+		git_error_state err;
+
+		giterr_capture(&err, ret);
+
+		ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path);
+		if (ret == GIT_ENOTFOUND)
+			return giterr_restore(&err);
+		else
+			git__free(err.error_msg.message);
+
+		ret = git_submodule_add_to_index(sm, false);
+		git_submodule_free(sm);
+		return ret;
+	}
+
 	/* Adding implies conflict was resolved, move conflict entries to REUC */
 	if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
 		return ret;
diff --git a/src/khash.h b/src/khash.h
index 818ac83..71eb583 100644
--- a/src/khash.h
+++ b/src/khash.h
@@ -619,4 +619,4 @@
 #define KHASH_MAP_INIT_STR(name, khval_t)								\
 	KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
 
-#endif /* __AC_KHASH_H */
\ No newline at end of file
+#endif /* __AC_KHASH_H */
diff --git a/src/merge.c b/src/merge.c
index 9d6252e..863ac8f 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -79,7 +79,7 @@
 	unsigned int i;
 
 	if (length < 2) {
-		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
+		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
 		return -1;
 	}
 
@@ -185,7 +185,7 @@
 	assert(out && repo && input_array);
 
 	if (length < 2) {
-		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
+		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
 		return -1;
 	}
 
@@ -2451,7 +2451,7 @@
 		goto done;
 
 	if ((conflicts = index_conflicts + wd_conflicts) > 0) {
-		giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+		giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
 			conflicts, (conflicts != 1) ? "s" : "");
 		error = GIT_ECONFLICT;
 	}
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 958252e..4df7c6b 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -324,7 +324,9 @@
 
 	SSL_set_bio(st->ssl, bio, bio);
 	/* specify the host in case SNI is needed */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	SSL_set_tlsext_host_name(st->ssl, st->host);
+#endif
 
 	if ((ret = SSL_connect(st->ssl)) <= 0)
 		return ssl_set_error(st->ssl, ret);
diff --git a/src/path.c b/src/path.c
index 2558058..3e63f06 100644
--- a/src/path.c
+++ b/src/path.c
@@ -12,6 +12,7 @@
 #include "win32/posix.h"
 #include "win32/buffer.h"
 #include "win32/w32_util.h"
+#include "win32/version.h"
 #else
 #include <dirent.h>
 #endif
@@ -1085,7 +1086,7 @@
 #if defined(GIT_WIN32) && !defined(__MINGW32__)
 
 /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
- * and better.  Prior versions will ignore this.
+ * and better.
  */
 #ifndef FIND_FIRST_EX_LARGE_FETCH
 # define FIND_FIRST_EX_LARGE_FETCH 2
@@ -1099,6 +1100,10 @@
 	git_win32_path path_filter;
 	git_buf hack = {0};
 
+	static int is_win7_or_later = -1;
+	if (is_win7_or_later < 0)
+		is_win7_or_later = git_has_win32_version(6, 1, 0);
+
 	assert(diriter && path);
 
 	memset(diriter, 0, sizeof(git_path_diriter));
@@ -1122,11 +1127,11 @@
 
 	diriter->handle = FindFirstFileExW(
 		path_filter,
-		FindExInfoBasic,
+		is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
 		&diriter->current,
 		FindExSearchNameMatch,
 		NULL,
-		FIND_FIRST_EX_LARGE_FETCH);
+		is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
 
 	if (diriter->handle == INVALID_HANDLE_VALUE) {
 		giterr_set(GITERR_OS, "Could not open directory '%s'", path);
diff --git a/src/rebase.c b/src/rebase.c
index 8da7b4f..17536c0 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -436,7 +436,7 @@
 	size_t i;
 	int error = 0;
 
-	if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 ||
+	if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
 		(error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
 		goto done;
 
@@ -789,7 +789,7 @@
 	normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
 
 	if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
-		(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
+		(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
 		(error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
 		(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
 		(error = git_merge__check_result(rebase->repo, index)) < 0 ||
diff --git a/src/stash.c b/src/stash.c
index acf8944..fcb1112 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -770,7 +770,7 @@
 		goto done;
 
 	if (git_diff_num_deltas(index_diff) > 0) {
-		giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index",
+		giterr_set(GITERR_STASH, "%" PRIuZ " uncommitted changes exist in the index",
 			git_diff_num_deltas(index_diff));
 		error = GIT_EUNCOMMITTED;
 	}
diff --git a/src/submodule.c b/src/submodule.c
index fb3d4bf..3d02874 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -781,11 +781,25 @@
 int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
 {
 	int error = 0;
+	git_buf normalized = GIT_BUF_INIT;
 
 	assert(out && repo && url);
 
 	git_buf_sanitize(out);
 
+	if (strchr(url, '\\')) {
+		char *p;
+		if ((error = git_buf_puts(&normalized, url)) < 0)
+			return error;
+
+		for (p = normalized.ptr; *p; p++) {
+			if (*p == '\\')
+				*p = '/';
+		}
+
+		url = normalized.ptr;
+	}
+
 	if (git_path_is_relative(url)) {
 		if (!(error = get_url_base(out, repo)))
 			error = git_path_apply_relative(out, url);
@@ -796,6 +810,7 @@
 		error = -1;
 	}
 
+	git_buf_free(&normalized);
 	return error;
 }
 
@@ -1647,7 +1662,7 @@
 	} else {
 		khiter_t pos;
 		git_strmap *map = data->map;
-		pos = git_strmap_lookup_index(map, name.ptr);
+		pos = git_strmap_lookup_index(map, path ? path : name.ptr);
 		if (git_strmap_valid_index(map, pos)) {
 			sm = git_strmap_value_at(map, pos);
 		} else {
diff --git a/src/thread-utils.c b/src/thread-utils.c
index c3baf41..dc9b2f0 100644
--- a/src/thread-utils.c
+++ b/src/thread-utils.c
@@ -8,7 +8,9 @@
 #include "thread-utils.h"
 
 #ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
 #	define WIN32_LEAN_AND_MEAN
+#endif
 #	include <windows.h>
 #elif defined(hpux) || defined(__hpux) || defined(_hpux)
 #	include <sys/pstat.h>
diff --git a/src/transports/http.c b/src/transports/http.c
index dd44264..1ed292b 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -511,7 +511,7 @@
 	git_buf buf = GIT_BUF_INIT;
 
 	/* Chunk header */
-	git_buf_printf(&buf, "%X\r\n", (unsigned)len);
+	git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
 
 	if (git_buf_oom(&buf))
 		return -1;
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index d214c9f..9ccbd80 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -523,7 +523,7 @@
 
 	if (len > 0xffff) {
 		giterr_set(GITERR_NET,
-			"Tried to produce packet with invalid length %d", len);
+			"Tried to produce packet with invalid length %" PRIuZ, len);
 		return -1;
 	}
 
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
index cb8b235..e2f1e89 100644
--- a/src/xdiff/xdiff.h
+++ b/src/xdiff/xdiff.h
@@ -32,14 +32,14 @@
 #define XDF_IGNORE_WHITESPACE (1 << 2)
 #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
 #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
 #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
 
-#define XDL_PATCH_NORMAL '-'
-#define XDL_PATCH_REVERSE '+'
-#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
-#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
+#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
+
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
@@ -88,13 +88,17 @@
 
 typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
 
+typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
+					    long start_b, long count_b,
+					    void *cb_data);
+
 typedef struct s_xdemitconf {
 	long ctxlen;
 	long interhunkctxlen;
 	unsigned long flags;
 	find_func_t find_func;
 	void *find_func_priv;
-	void (*emit_func)(void);
+	xdl_emit_hunk_consume_func_t hunk_func;
 } xdemitconf_t;
 
 typedef struct s_bdiffparam {
diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c
index 84aa0fc..0620e5f 100644
--- a/src/xdiff/xdiffi.c
+++ b/src/xdiff/xdiffi.c
@@ -328,10 +328,10 @@
 	xdalgoenv_t xenv;
 	diffdata_t dd1, dd2;
 
-	if (xpp->flags & XDF_PATIENCE_DIFF)
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
 		return xdl_do_patience_diff(mf1, mf2, xpp, xe);
 
-	if (xpp->flags & XDF_HISTOGRAM_DIFF)
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
 		return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
 
 	if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
@@ -394,6 +394,7 @@
 	xch->i2 = i2;
 	xch->chg1 = chg1;
 	xch->chg2 = chg2;
+	xch->ignore = 0;
 
 	return xch;
 }
@@ -538,13 +539,51 @@
 	}
 }
 
+static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+			      xdemitconf_t const *xecfg)
+{
+	xdchange_t *xch, *xche;
+
+	(void)xe;
+
+	for (xch = xscr; xch; xch = xche->next) {
+		xche = xdl_get_hunk(&xch, xecfg);
+		if (!xch)
+			break;
+		if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
+				     xch->i2, xche->i2 + xche->chg2 - xch->i2,
+				     ecb->priv) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+	xdchange_t *xch;
+
+	for (xch = xscr; xch; xch = xch->next) {
+		int ignore = 1;
+		xrecord_t **rec;
+		long i;
+
+		rec = &xe->xdf1.recs[xch->i1];
+		for (i = 0; i < xch->chg1 && ignore; i++)
+			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+		rec = &xe->xdf2.recs[xch->i2];
+		for (i = 0; i < xch->chg2 && ignore; i++)
+			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+		xch->ignore = ignore;
+	}
+}
 
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
 	xdchange_t *xscr;
 	xdfenv_t xe;
-	emit_func_t ef = xecfg->emit_func ?
-		(emit_func_t)xecfg->emit_func : xdl_emit_diff;
+	emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
 
 	if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
 
@@ -558,6 +597,9 @@
 		return -1;
 	}
 	if (xscr) {
+		if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+			xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
 		if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
 			xdl_free_script(xscr);
diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h
index 7a92ea9..8b81206 100644
--- a/src/xdiff/xdiffi.h
+++ b/src/xdiff/xdiffi.h
@@ -41,6 +41,7 @@
 	struct s_xdchange *next;
 	long i1, i2;
 	long chg1, chg2;
+	int ignore;
 } xdchange_t;
 
 
diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c
index e3e63d9..600fd1f 100644
--- a/src/xdiff/xemit.c
+++ b/src/xdiff/xemit.c
@@ -56,16 +56,51 @@
 /*
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
  */
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
-	xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+	xdchange_t *xch, *xchp, *lxch;
 	long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+	long max_ignorable = xecfg->ctxlen;
+	unsigned long ignored = 0; /* number of ignored blank lines */
 
-	for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-		if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+	/* remove ignorable changes that are too far before other changes */
+	for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+		xch = xchp->next;
+
+		if (xch == NULL ||
+		    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+			*xscr = xch;
+	}
+
+	if (*xscr == NULL)
+		return NULL;
+
+	lxch = *xscr;
+
+	for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+		long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+		if (distance > max_common)
 			break;
 
-	return xchp;
+		if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+			lxch = xch;
+			ignored = 0;
+		} else if (distance < max_ignorable && xch->ignore) {
+			ignored += xch->chg2;
+		} else if (lxch != xchp &&
+			   xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) {
+			break;
+		} else if (!xch->ignore) {
+			lxch = xch;
+			ignored = 0;
+		} else {
+			ignored += xch->chg2;
+		}
+	}
+
+	return lxch;
 }
 
 
@@ -144,7 +179,9 @@
 		return xdl_emit_common(xe, xscr, ecb, xecfg);
 
 	for (xch = xscr; xch; xch = xche->next) {
-		xche = xdl_get_hunk(xch, xecfg);
+		xche = xdl_get_hunk(&xch, xecfg);
+		if (!xch)
+			break;
 
 		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
 		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h
index c2e2e83..d297107 100644
--- a/src/xdiff/xemit.h
+++ b/src/xdiff/xemit.h
@@ -27,7 +27,7 @@
 typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 			   xdemitconf_t const *xecfg);
 
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg);
 
diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c
index c848128..500d811 100644
--- a/src/xdiff/xhistogram.c
+++ b/src/xdiff/xhistogram.c
@@ -258,7 +258,7 @@
 		int line1, int count1, int line2, int count2)
 {
 	xpparam_t xpp;
-	xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+	xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
 	return xdl_fall_back_diff(index->env, &xpp,
 				  line1, count1, line2, count2);
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
index 84e4246..b11e598 100644
--- a/src/xdiff/xmerge.c
+++ b/src/xdiff/xmerge.c
@@ -245,11 +245,11 @@
 					      dest ? dest + size : NULL);
 			/* Postimage from side #1 */
 			if (m->mode & 1)
-				size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+				size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
 						      dest ? dest + size : NULL);
 			/* Postimage from side #2 */
 			if (m->mode & 2)
-				size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+				size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
 						      dest ? dest + size : NULL);
 		} else
 			continue;
diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c
index fdd7d02..04e1a1a 100644
--- a/src/xdiff/xpatience.c
+++ b/src/xdiff/xpatience.c
@@ -288,7 +288,7 @@
 		int line1, int count1, int line2, int count2)
 {
 	xpparam_t xpp;
-	xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+	xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
 	return xdl_fall_back_diff(map->env, &xpp,
 				  line1, count1, line2, count2);
diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c
index e419f4f..63a22c6 100644
--- a/src/xdiff/xprepare.c
+++ b/src/xdiff/xprepare.c
@@ -181,7 +181,7 @@
 	if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
 		goto abort;
 
-	if (xpp->flags & XDF_HISTOGRAM_DIFF)
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
 		hbits = hsize = 0;
 	else {
 		hbits = xdl_hashbits((unsigned int) narec);
@@ -209,8 +209,8 @@
 			crec->ha = hav;
 			recs[nrec++] = crec;
 
-			if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-				xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+			if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+			    xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
 				goto abort;
 		}
 	}
@@ -273,16 +273,15 @@
 	 * (nrecs) will be updated correctly anyway by
 	 * xdl_prepare_ctx().
 	 */
-	sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
 
 	enl1 = xdl_guess_lines(mf1, sample) + 1;
 	enl2 = xdl_guess_lines(mf2, sample) + 1;
 
-	if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-		xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
-
+	if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
+	    xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
 		return -1;
-	}
 
 	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
 
@@ -296,9 +295,9 @@
 		return -1;
 	}
 
-	if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
-			!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-			xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
 
 		xdl_free_ctx(&xe->xdf2);
 		xdl_free_ctx(&xe->xdf1);
diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c
index bb7bdee..30f2a30 100644
--- a/src/xdiff/xutils.c
+++ b/src/xdiff/xutils.c
@@ -120,35 +120,6 @@
 	return data;
 }
 
-
-void *xdl_cha_first(chastore_t *cha) {
-	chanode_t *sncur;
-
-	if (!(cha->sncur = sncur = cha->head))
-		return NULL;
-
-	cha->scurr = 0;
-
-	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-void *xdl_cha_next(chastore_t *cha) {
-	chanode_t *sncur;
-
-	if (!(sncur = cha->sncur))
-		return NULL;
-	cha->scurr += cha->isize;
-	if (cha->scurr == sncur->icurr) {
-		if (!(sncur = cha->sncur = sncur->next))
-			return NULL;
-		cha->scurr = 0;
-	}
-
-	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
 long xdl_guess_lines(mmfile_t *mf, long sample) {
 	long nl = 0, size, tsize = 0;
 	char const *data, *cur, *top;
@@ -170,6 +141,19 @@
 	return nl + 1;
 }
 
+int xdl_blankline(const char *line, long size, long flags)
+{
+	long i;
+
+	if (!(flags & XDF_WHITESPACE_FLAGS))
+		return (size <= 1);
+
+	for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+		;
+
+	return (i == size);
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
 	int i1, i2;
diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h
index 714719a..8f952a8 100644
--- a/src/xdiff/xutils.h
+++ b/src/xdiff/xutils.h
@@ -34,6 +34,7 @@
 void *xdl_cha_first(chastore_t *cha);
 void *xdl_cha_next(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);
diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c
index 21cd1a6..b305ba1 100644
--- a/tests/blame/blame_helpers.c
+++ b/tests/blame/blame_helpers.c
@@ -4,7 +4,7 @@
 {
 	va_list arglist;
 
-	printf("Hunk %zd (line %d +%d): ", idx,
+	printf("Hunk %"PRIuZ" (line %d +%d): ", idx,
 			hunk->final_start_line_number, hunk->lines_in_hunk-1);
 
 	va_start(arglist, fmt);
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index dabc47a..b14af44 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -483,8 +483,8 @@
 			for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
 				/* find differing byte offset */;
 			p_snprintf(
-				buf, sizeof(buf), "file content mismatch at byte %d",
-				(int)(total_bytes + pos));
+				buf, sizeof(buf), "file content mismatch at byte %"PRIdZ,
+				(ssize_t)(total_bytes + pos));
 			p_close(fd);
 			clar__fail(file, line, path, buf, 1);
 		}
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
new file mode 100644
index 0000000..ddb766a
--- /dev/null
+++ b/tests/index/bypath.c
@@ -0,0 +1,35 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo;
+static git_index *g_idx;
+
+void test_index_bypath__initialize(void)
+{
+	g_repo = setup_fixture_submod2();
+	cl_git_pass(git_repository_index__weakptr(&g_idx, g_repo));
+}
+
+void test_index_bypath__cleanup(void)
+{
+	g_repo = NULL;
+	g_idx = NULL;
+}
+
+void test_index_bypath__add_directory(void)
+{
+	cl_git_fail_with(GIT_EDIRECTORY, git_index_add_bypath(g_idx, "just_a_dir"));
+}
+
+void test_index_bypath__add_submodule(void)
+{
+	unsigned int status;
+	const char *sm_name = "sm_changed_head";
+
+	cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+	cl_assert_equal_i(GIT_SUBMODULE_STATUS_WD_MODIFIED, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+	cl_git_pass(git_index_add_bypath(g_idx, sm_name));
+	cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+	cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+}
diff --git a/tests/merge/files.c b/tests/merge/files.c
index 7f461ab..2fd90d0 100644
--- a/tests/merge/files.c
+++ b/tests/merge/files.c
@@ -249,3 +249,42 @@
 
 	git_merge_file_result_free(&result);
 }
+
+void test_merge_files__doesnt_add_newline(void)
+{
+	git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+		ours = GIT_MERGE_FILE_INPUT_INIT,
+		theirs = GIT_MERGE_FILE_INPUT_INIT;
+	git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+	git_merge_file_result result = {0};
+	const char *expected = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen";
+
+	ancestor.ptr = "0\n1\n2\n3\n4\n5 XXX\n6YYY\n7\n8\n9\n10";
+	ancestor.size = strlen(ancestor.ptr);
+	ancestor.path = "testfile.txt";
+	ancestor.mode = 0100755;
+
+	ours.ptr = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\n10";
+	ours.size = strlen(ours.ptr);
+	ours.path = "testfile.txt";
+	ours.mode = 0100755;
+
+	theirs.ptr = "0\n1\n2\n3\n4\n5 XXX\n6  YYY\n7\n8\n9\nTen";
+	theirs.size = strlen(theirs.ptr);
+	theirs.path = "testfile.txt";
+	theirs.mode = 0100755;
+
+	opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE;
+	cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+	cl_assert_equal_i(1, result.automergeable);
+
+	cl_assert_equal_s("testfile.txt", result.path);
+	cl_assert_equal_i(0100755, result.mode);
+
+	cl_assert_equal_i(strlen(expected), result.len);
+	cl_assert_equal_strn(expected, result.ptr, result.len);
+
+	git_merge_file_result_free(&result);
+}
+
diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c
index 33710f4..f814714 100644
--- a/tests/merge/merge_helpers.c
+++ b/tests/merge/merge_helpers.c
@@ -110,7 +110,7 @@
 	size_t i;
 	const git_index_entry *index_entry;
 
-	printf ("\nINDEX [%d]:\n", (int)index_entries->length);
+	printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length);
 	for (i = 0; i < index_entries->length; i++) {
 		index_entry = index_entries->contents[i];
 
diff --git a/tests/revert/workdir.c b/tests/revert/workdir.c
index 7ccf0f9..9f83bd8 100644
--- a/tests/revert/workdir.c
+++ b/tests/revert/workdir.c
@@ -334,16 +334,18 @@
 	cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
 
 	cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt"));
-	cl_assert(strcmp(diff_buf.ptr,	"a\n" \
-		"<<<<<<< HEAD\n" \
-		"=======\n" \
-		"a\n" \
-		">>>>>>> parent of 97e52d5... Revert me\n" \
-		"a\n" \
-		"a\n" \
-		"a\n" \
-		"a\n" \
-		"ab\n") == 0);
+	cl_assert_equal_s(
+			"a\n" \
+			"<<<<<<< HEAD\n" \
+			"=======\n" \
+			"a\n" \
+			">>>>>>> parent of 97e52d5... Revert me\n" \
+			"a\n" \
+			"a\n" \
+			"a\n" \
+			"a\n" \
+			"ab",
+		diff_buf.ptr);
 
 	git_commit_free(revert_commit);
 	git_commit_free(head_commit);
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index 01625d3..c3b3e63 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -4,6 +4,7 @@
 #include "submodule_helpers.h"
 #include "config/config_helpers.h"
 #include "fileops.h"
+#include "repository.h"
 
 static git_repository *g_repo = NULL;
 
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
index 4d40e22..ecea694 100644
--- a/tests/submodule/lookup.c
+++ b/tests/submodule/lookup.c
@@ -1,6 +1,7 @@
 #include "clar_libgit2.h"
 #include "submodule_helpers.h"
 #include "git2/sys/repository.h"
+#include "repository.h"
 #include "fileops.h"
 
 static git_repository *g_repo = NULL;
@@ -103,10 +104,27 @@
 
 void test_submodule_lookup__foreach(void)
 {
+	git_config *cfg;
 	sm_lookup_data data;
+
 	memset(&data, 0, sizeof(data));
 	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
 	cl_assert_equal_i(8, data.count);
+
+	memset(&data, 0, sizeof(data));
+
+	/* Change the path for a submodule so it doesn't match the name */
+	cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+
+	cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
+	cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
+	cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
+	cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));
+
+	git_config_free(cfg);
+
+	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+	cl_assert_equal_i(8, data.count);
 }
 
 void test_submodule_lookup__lookup_even_with_unborn_head(void)
@@ -133,6 +151,29 @@
 	test_submodule_lookup__simple_lookup(); /* baseline should still pass */
 }
 
+void test_submodule_lookup__backslashes(void)
+{
+	git_config *cfg;
+	git_submodule *sm;
+	git_repository *subrepo;
+	git_buf buf = GIT_BUF_INIT;
+	const char *backslashed_path = "..\\submod2_target";
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+	cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.url", backslashed_path));
+	git_config_free(cfg);
+
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_assert_equal_s(backslashed_path, git_submodule_url(sm));
+	cl_git_pass(git_submodule_open(&subrepo, sm));
+
+	cl_git_pass(git_submodule_resolve_url(&buf, g_repo, backslashed_path));
+
+	git_buf_free(&buf);
+	git_submodule_free(sm);
+	git_repository_free(subrepo);
+}
+
 static void baseline_tests(void)
 {
 	/* small baseline that should work even if we change the index or make