提交 8eaf5178 编写于 作者: J Junio C Hamano

Merge branch 'ks/tree-diff-nway'

Instead of running N pair-wise diff-trees when inspecting a
N-parent merge, find the set of paths that were touched by walking
N+1 trees in parallel.  These set of paths can then be turned into
N pair-wise diff-tree results to be processed through rename
detections and such.  And N=2 case nicely degenerates to the usual
2-way diff-tree, which is very nice.

* ks/tree-diff-nway:
  mingw: activate alloca
  combine-diff: speed it up, by using multiparent diff tree-walker directly
  tree-diff: rework diff_tree() to generate diffs for multiparent cases as well
  Portable alloca for Git
  tree-diff: reuse base str(buf) memory on sub-tree recursion
  tree-diff: no need to call "full" diff_tree_sha1 from show_path()
  tree-diff: rework diff_tree interface to be sha1 based
  tree-diff: diff_tree() should now be static
  tree-diff: remove special-case diff-emitting code for empty-tree cases
  tree-diff: simplify tree_entry_pathcmp
  tree-diff: show_path prototype is not needed anymore
  tree-diff: rename compare_tree_entry -> tree_entry_pathcmp
  tree-diff: move all action-taking code out of compare_tree_entry()
  tree-diff: don't assume compare_tree_entry() returns -1,0,1
  tree-diff: consolidate code for emitting diffs and recursion in one place
  tree-diff: show_tree() is not needed
  tree-diff: no need to pass match to skip_uninteresting()
  tree-diff: no need to manually verify that there is no mode change for a path
  combine-diff: move changed-paths scanning logic into its own function
  combine-diff: move show_log_first logic/action out of paths scanning
......@@ -30,6 +30,8 @@ all::
# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
#
# Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
#
# Define NO_CURL if you do not have libcurl installed. git-http-fetch and
# git-http-push are not built, and you cannot use http:// and https://
# transports (neither smart nor dumb).
......@@ -1111,6 +1113,10 @@ ifdef USE_LIBPCRE
EXTLIBS += -lpcre
endif
ifdef HAVE_ALLOCA_H
BASIC_CFLAGS += -DHAVE_ALLOCA_H
endif
ifdef NO_CURL
BASIC_CFLAGS += -DNO_CURL
REMOTE_CURL_PRIMARY =
......
......@@ -74,6 +74,21 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
#define S_IFGITLINK 0160000
#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
/*
* Some mode bits are also used internally for computations.
*
* They *must* not overlap with any valid modes, and they *must* not be emitted
* to outside world - i.e. appear on disk or network. In other words, it's just
* temporary fields, which we internally use, but they have to stay in-house.
*
* ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
* codebase mode is `unsigned int` which is assumed to be at least 32 bits )
*/
/* used internally in tree-diff */
#define S_DIFFTREE_IFXMIN_NEQ 0x80000000
/*
* Intensive research over the course of many years has shown that
* port 9418 is totally unused by anything else. Or
......
......@@ -1301,6 +1301,81 @@ static const char *path_path(void *obj)
return path->path;
}
/* find set of paths that every parent touches */
static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
const struct sha1_array *parents, struct diff_options *opt)
{
struct combine_diff_path *paths = NULL;
int i, num_parent = parents->nr;
int output_format = opt->output_format;
const char *orderfile = opt->orderfile;
opt->output_format = DIFF_FORMAT_NO_OUTPUT;
/* tell diff_tree to emit paths in sorted (=tree) order */
opt->orderfile = NULL;
/* D(A,P1...Pn) = D(A,P1) ^ ... ^ D(A,Pn) (wrt paths) */
for (i = 0; i < num_parent; i++) {
/*
* show stat against the first parent even when doing
* combined diff.
*/
int stat_opt = (output_format &
(DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
if (i == 0 && stat_opt)
opt->output_format = stat_opt;
else
opt->output_format = DIFF_FORMAT_NO_OUTPUT;
diff_tree_sha1(parents->sha1[i], sha1, "", opt);
diffcore_std(opt);
paths = intersect_paths(paths, i, num_parent);
/* if showing diff, show it in requested order */
if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
orderfile) {
diffcore_order(orderfile);
}
diff_flush(opt);
}
opt->output_format = output_format;
opt->orderfile = orderfile;
return paths;
}
/*
* find set of paths that everybody touches, assuming diff is run without
* rename/copy detection, etc, comparing all trees simultaneously (= faster).
*/
static struct combine_diff_path *find_paths_multitree(
const unsigned char *sha1, const struct sha1_array *parents,
struct diff_options *opt)
{
int i, nparent = parents->nr;
const unsigned char **parents_sha1;
struct combine_diff_path paths_head;
struct strbuf base;
parents_sha1 = xmalloc(nparent * sizeof(parents_sha1[0]));
for (i = 0; i < nparent; i++)
parents_sha1[i] = parents->sha1[i];
/* fake list head, so worker can assume it is non-NULL */
paths_head.next = NULL;
strbuf_init(&base, PATH_MAX);
diff_tree_paths(&paths_head, sha1, parents_sha1, nparent, &base, opt);
strbuf_release(&base);
free(parents_sha1);
return paths_head.next;
}
void diff_tree_combined(const unsigned char *sha1,
const struct sha1_array *parents,
int dense,
......@@ -1308,49 +1383,82 @@ void diff_tree_combined(const unsigned char *sha1,
{
struct diff_options *opt = &rev->diffopt;
struct diff_options diffopts;
struct combine_diff_path *p, *paths = NULL;
struct combine_diff_path *p, *paths;
int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
int need_generic_pathscan;
/* nothing to do, if no parents */
if (!num_parent)
return;
show_log_first = !!rev->loginfo && !rev->no_commit_id;
needsep = 0;
if (show_log_first) {
show_log(rev);
if (rev->verbose_header && opt->output_format)
printf("%s%c", diff_line_prefix(opt),
opt->line_termination);
}
diffopts = *opt;
copy_pathspec(&diffopts.pathspec, &opt->pathspec);
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
DIFF_OPT_SET(&diffopts, RECURSIVE);
DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
/* tell diff_tree to emit paths in sorted (=tree) order */
diffopts.orderfile = NULL;
show_log_first = !!rev->loginfo && !rev->no_commit_id;
needsep = 0;
/* find set of paths that everybody touches */
for (i = 0; i < num_parent; i++) {
/* show stat against the first parent even
/* find set of paths that everybody touches
*
* NOTE
*
* Diffcore transformations are bound to diff_filespec and logic
* comparing two entries - i.e. they do not apply directly to combine
* diff.
*
* If some of such transformations is requested - we launch generic
* path scanning, which works significantly slower compared to
* simultaneous all-trees-in-one-go scan in find_paths_multitree().
*
* TODO some of the filters could be ported to work on
* combine_diff_paths - i.e. all functionality that skips paths, so in
* theory, we could end up having only multitree path scanning.
*
* NOTE please keep this semantically in sync with diffcore_std()
*/
need_generic_pathscan = opt->skip_stat_unmatch ||
DIFF_OPT_TST(opt, FOLLOW_RENAMES) ||
opt->break_opt != -1 ||
opt->detect_rename ||
opt->pickaxe ||
opt->filter;
if (need_generic_pathscan) {
/*
* NOTE generic case also handles --stat, as it computes
* diff(sha1,parent_i) for all i to do the job, specifically
* for parent0.
*/
paths = find_paths_generic(sha1, parents, &diffopts);
}
else {
int stat_opt;
paths = find_paths_multitree(sha1, parents, &diffopts);
/*
* show stat against the first parent even
* when doing combined diff.
*/
int stat_opt = (opt->output_format &
stat_opt = (opt->output_format &
(DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
if (i == 0 && stat_opt)
if (stat_opt) {
diffopts.output_format = stat_opt;
else
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_tree_sha1(parents->sha1[i], sha1, "", &diffopts);
diffcore_std(&diffopts);
paths = intersect_paths(paths, i, num_parent);
if (show_log_first && i == 0) {
show_log(rev);
if (rev->verbose_header && opt->output_format)
printf("%s%c", diff_line_prefix(opt),
opt->line_termination);
diff_tree_sha1(parents->sha1[0], sha1, "", &diffopts);
diffcore_std(&diffopts);
if (opt->orderfile)
diffcore_order(opt->orderfile);
diff_flush(&diffopts);
}
/* if showing diff, show it in requested order */
if (diffopts.output_format != DIFF_FORMAT_NO_OUTPUT &&
opt->orderfile) {
diffcore_order(opt->orderfile);
}
diff_flush(&diffopts);
}
/* find out number of surviving paths */
......
......@@ -28,6 +28,7 @@ ifeq ($(uname_S),OSF1)
NO_NSEC = YesPlease
endif
ifeq ($(uname_S),Linux)
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
......@@ -35,6 +36,7 @@ ifeq ($(uname_S),Linux)
HAVE_DEV_TTY = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
......@@ -103,6 +105,7 @@ ifeq ($(uname_S),SunOS)
NEEDS_NSL = YesPlease
SHELL_PATH = /bin/bash
SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin
HAVE_ALLOCA_H = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
......@@ -145,7 +148,7 @@ ifeq ($(uname_S),SunOS)
endif
INSTALL = /usr/ucb/install
TAR = gtar
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
endif
ifeq ($(uname_O),Cygwin)
ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
......@@ -165,6 +168,7 @@ ifeq ($(uname_O),Cygwin)
else
NO_REGEX = UnfortunatelyYes
endif
HAVE_ALLOCA_H = YesPlease
NEEDS_LIBICONV = YesPlease
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
......@@ -239,6 +243,7 @@ ifeq ($(uname_S),AIX)
endif
ifeq ($(uname_S),GNU)
# GNU/Hurd
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
......@@ -313,6 +318,7 @@ endif
ifeq ($(uname_S),Windows)
GIT_VERSION := $(GIT_VERSION).MSVC
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
NEEDS_CRYPTO_WITH_SSL = YesPlease
NO_LIBGEN_H = YesPlease
......@@ -357,7 +363,7 @@ ifeq ($(uname_S),Windows)
COMPAT_OBJS = compat/msvc.o compat/winansi.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/dirent.o
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
PTHREAD_LIBS =
......@@ -465,6 +471,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
endif
ifneq (,$(findstring MINGW,$(uname_S)))
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
NEEDS_CRYPTO_WITH_SSL = YesPlease
NO_LIBGEN_H = YesPlease
......
......@@ -272,6 +272,14 @@ AS_HELP_STRING([], [ARG can be also prefix for libpcre library and hea
GIT_CONF_SUBST([LIBPCREDIR])
fi)
#
# Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
AC_FUNC_ALLOCA
case $ac_cv_working_alloca_h in
yes) HAVE_ALLOCA_H=YesPlease;;
*) HAVE_ALLOCA_H='';;
esac
GIT_CONF_SUBST([HAVE_ALLOCA_H])
#
# Define NO_CURL if you do not have curl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# transports.
......
......@@ -3205,6 +3205,7 @@ void diff_setup(struct diff_options *options)
options->context = diff_context_default;
DIFF_OPT_SET(options, RENAME_EMPTY);
/* pathchange left =NULL by default */
options->change = diff_change;
options->add_remove = diff_addremove;
options->use_color = diff_use_color_default;
......@@ -4749,6 +4750,7 @@ void diffcore_fix_diff_index(struct diff_options *options)
void diffcore_std(struct diff_options *options)
{
/* NOTE please keep the following in sync with diff_tree_combined() */
if (options->skip_stat_unmatch)
diffcore_skip_stat_unmatch(options);
if (!options->found_follow) {
......
......@@ -15,6 +15,10 @@ struct diff_filespec;
struct userdiff_driver;
struct sha1_array;
struct commit;
struct combine_diff_path;
typedef int (*pathchange_fn_t)(struct diff_options *options,
struct combine_diff_path *path);
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
......@@ -157,6 +161,7 @@ struct diff_options {
int close_file;
struct pathspec pathspec;
pathchange_fn_t pathchange;
change_fn_t change;
add_remove_fn_t add_remove;
diff_format_fn_t format_callback;
......@@ -189,8 +194,10 @@ const char *diff_line_prefix(struct diff_options *);
extern const char mime_boundary_leader[];
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
const char *base, struct diff_options *opt);
extern struct combine_diff_path *diff_tree_paths(
struct combine_diff_path *p, const unsigned char *sha1,
const unsigned char **parent_sha1, int nparent,
struct strbuf *base, struct diff_options *opt);
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
const char *base, struct diff_options *opt);
extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
......
......@@ -521,6 +521,14 @@ extern void release_pack_memory(size_t);
typedef void (*try_to_free_t)(size_t);
extern try_to_free_t set_try_to_free_routine(try_to_free_t);
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
# define xalloca(size) (alloca(size))
# define xalloca_free(p) do {} while (0)
#else
# define xalloca(size) (xmalloc(size))
# define xalloca_free(p) (free(p))
#endif
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
extern void *xmallocz(size_t size);
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册