static int no_commit_id = 0;
static int verbose_header = 0;
static int ignore_merges = 1;
++ ++static int show_empty_combined = 0;
++ ++static int combine_merges = 0;
++ ++static int dense_combined_merges = 0;
static int read_stdin = 0;
static const char *header = NULL;
static const char *generate_header(const unsigned char *commit_sha1,
const unsigned char *parent_sha1,
---- const char *msg)
++++ const struct commit *commit)
{
static char this_header[16384];
int offset;
unsigned long len;
int abbrev = diff_options.abbrev;
++++ const char *msg = commit->buffer;
if (!verbose_header)
return sha1_to_hex(commit_sha1);
offset = sprintf(this_header, "%s%s ",
header_prefix,
diff_unique_abbrev(commit_sha1, abbrev));
-- -- offset += sprintf(this_header + offset, "(from %s)\n",
-- -- parent_sha1 ?
-- -- diff_unique_abbrev(parent_sha1, abbrev) : "root");
-- - offset += pretty_print_commit(commit_format, msg, len,
++ ++ if (commit_sha1 != parent_sha1)
++ ++ offset += sprintf(this_header + offset, "(from %s)\n",
++ ++ parent_sha1
++ ++ ? diff_unique_abbrev(parent_sha1, abbrev)
++ ++ : "root");
++ ++ else
++ ++ offset += sprintf(this_header + offset, "(from parents)\n");
- offset += pretty_print_commit(commit_format, msg, len,
++++ offset += pretty_print_commit(commit_format, commit, len,
this_header + offset,
---- sizeof(this_header) - offset);
++++ sizeof(this_header) - offset, abbrev);
return this_header;
}
/* Root commit? */
if (show_root_diff && !commit->parents) {
---- header = generate_header(sha1, NULL, commit->buffer);
++++ header = generate_header(sha1, NULL, commit);
diff_root_tree(commit_sha1, "");
}
/* More than one parent? */
-- -- if (ignore_merges && commit->parents && commit->parents->next)
-- -- return 0;
++ ++ if (commit->parents && commit->parents->next) {
++ ++ if (ignore_merges)
++ ++ return 0;
++ ++ else if (combine_merges) {
- header = generate_header(sha1, sha1,
- commit->buffer);
+++++ header = generate_header(sha1, sha1, commit);
++ ++ return diff_tree_combined_merge(sha1, header,
++ ++ show_empty_combined,
++ ++ dense_combined_merges);
++ ++ }
++ ++ }
for (parents = commit->parents; parents; parents = parents->next) {
struct commit *parent = parents->item;
---- header = generate_header(sha1,
---- parent->object.sha1,
---- commit->buffer);
++++ header = generate_header(sha1, parent->object.sha1, commit);
diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
if (!header && verbose_header) {
header_prefix = "\ndiff-tree ";
}
static const char diff_tree_usage[] =
-- --"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] "
++ ++"git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
" -r diff recursively\n"
" --root include the initial commit as diff against /dev/null\n"
ignore_merges = 0;
continue;
}
++ ++ if (!strcmp(arg, "-c")) {
++ ++ combine_merges = 1;
++ ++ continue;
++ ++ }
++ ++ if (!strcmp(arg, "--cc")) {
++ ++ dense_combined_merges = combine_merges = 1;
++ ++ continue;
++ ++ }
if (!strcmp(arg, "-v")) {
verbose_header = 1;
header_prefix = "diff-tree ";
if (diff_options.output_format == DIFF_FORMAT_PATCH)
diff_options.recursive = 1;
++ ++ if (combine_merges) {
++ ++ diff_options.output_format = DIFF_FORMAT_PATCH;
++ ++ show_empty_combined = !ignore_merges;
++ ++ ignore_merges = 0;
++ ++ }
++ ++
diff_tree_setup_paths(get_pathspec(prefix, argv));
diff_setup_done(&diff_options);
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
const char *base, struct diff_options *opt);
++ ++struct combine_diff_path {
++ ++ struct combine_diff_path *next;
++ ++ int len;
++ ++ char *path;
++ ++ unsigned char sha1[20];
++ ++ unsigned char parent_sha1[FLEX_ARRAY][20];
++ ++};
++ ++
++ ++int show_combined_diff(struct combine_diff_path *elem, int num_parent,
++ ++ int dense, const char *header, int show_empty);
++ ++
++ ++extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int, int);
++ ++
extern void diff_addremove(struct diff_options *,
int addremove,
unsigned mode,
#define DIFF_PICKAXE_ALL 1
---- #define DIFF_DEFAULT_INDEX_ABBREV 7 /* hex digits */
---- #define DIFF_DEFAULT_ABBREV 7 /* hex digits */
----
extern void diffcore_std(struct diff_options *);
extern void diffcore_std_no_resolve(struct diff_options *);
" --min-age=epoch\n"
" --sparse\n"
" --no-merges\n"
+ +++" --remove-empty\n"
" --all\n"
" ordering output:\n"
" --merge-order [ --show-breaks ]\n"
static int topo_order = 0;
static int no_merges = 0;
static const char **paths = NULL;
+ +++static int remove_empty_trees = 0;
static void show_commit(struct commit *commit)
{
if (verbose_header) {
static char pretty_header[16384];
---- pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
++++ pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), 0);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);
}
}
- ---static int is_different = 0;
+ +++#define TREE_SAME 0
+ +++#define TREE_NEW 1
+ +++#define TREE_DIFFERENT 2
+ +++static int tree_difference = TREE_SAME;
static void file_add_remove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
const char *base, const char *path)
{
- --- is_different = 1;
+ +++ int diff = TREE_DIFFERENT;
+ +++
+ +++ /*
+ +++ * Is it an add of a new file? It means that
+ +++ * the old tree didn't have it at all, so we
+ +++ * will turn "TREE_SAME" -> "TREE_NEW", but
+ +++ * leave any "TREE_DIFFERENT" alone (and if
+ +++ * it already was "TREE_NEW", we'll keep it
+ +++ * "TREE_NEW" of course).
+ +++ */
+ +++ if (addremove == '+') {
+ +++ diff = tree_difference;
+ +++ if (diff != TREE_SAME)
+ +++ return;
+ +++ diff = TREE_NEW;
+ +++ }
+ +++ tree_difference = diff;
}
static void file_change(struct diff_options *options,
const unsigned char *new_sha1,
const char *base, const char *path)
{
- --- is_different = 1;
+ +++ tree_difference = TREE_DIFFERENT;
}
static struct diff_options diff_opt = {
.change = file_change,
};
- ---static int same_tree(struct tree *t1, struct tree *t2)
+ +++static int compare_tree(struct tree *t1, struct tree *t2)
{
- --- is_different = 0;
+ +++ if (!t1)
+ +++ return TREE_NEW;
+ +++ if (!t2)
+ +++ return TREE_DIFFERENT;
+ +++ tree_difference = TREE_SAME;
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
- --- return 0;
- --- return !is_different;
+ +++ return TREE_DIFFERENT;
+ +++ return tree_difference;
}
static int same_tree_as_empty(struct tree *t1)
empty.buf = "";
empty.size = 0;
- --- is_different = 0;
+ +++ tree_difference = 0;
retval = diff_tree(&empty, &real, "", &diff_opt);
free(tree);
- --- return retval >= 0 && !is_different;
+ +++ return retval >= 0 && !tree_difference;
}
- ---static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)
+ +++static void try_to_simplify_commit(struct commit *commit)
{
+ +++ struct commit_list **pp, *parent;
+ +++
if (!commit->tree)
- --- return NULL;
+ +++ return;
- --- while (parent) {
+ +++ if (!commit->parents) {
+ +++ if (!same_tree_as_empty(commit->tree))
+ +++ commit->object.flags |= TREECHANGE;
+ +++ return;
+ +++ }
+ +++
+ +++ pp = &commit->parents;
+ +++ while ((parent = *pp) != NULL) {
struct commit *p = parent->item;
- --- parent = parent->next;
+ +++
+ +++ if (p->object.flags & UNINTERESTING) {
+ +++ pp = &parent->next;
+ +++ continue;
+ +++ }
+ +++
parse_commit(p);
- --- if (!p->tree)
+ +++ switch (compare_tree(p->tree, commit->tree)) {
+ +++ case TREE_SAME:
+ +++ parent->next = NULL;
+ +++ commit->parents = parent;
+ +++ return;
+ +++
+ +++ case TREE_NEW:
+ +++ if (remove_empty_trees && same_tree_as_empty(p->tree)) {
+ +++ *pp = parent->next;
+ +++ continue;
+ +++ }
+ +++ /* fallthrough */
+ +++ case TREE_DIFFERENT:
+ +++ pp = &parent->next;
continue;
- --- if (same_tree(commit->tree, p->tree))
- --- return p;
+ +++ }
+ +++ die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
}
- --- return NULL;
+ +++ commit->object.flags |= TREECHANGE;
}
static void add_parents_to_list(struct commit *commit, struct commit_list **list)
}
/*
- --- * Ok, the commit wasn't uninteresting. If it
- --- * is a merge, try to find the parent that has
- --- * no differences in the path set if one exists.
+ +++ * Ok, the commit wasn't uninteresting. Try to
+ +++ * simplify the commit history and find the parent
+ +++ * that has no differences in the path set if one exists.
*/
- --- if (paths && parent && parent->next) {
- --- struct commit *preferred;
- ---
- --- preferred = try_to_simplify_merge(commit, parent);
- --- if (preferred) {
- --- parent->item = preferred;
- --- parent->next = NULL;
- --- }
- --- }
+ +++ if (paths)
+ +++ try_to_simplify_commit(commit);
+ +++ parent = commit->parents;
while (parent) {
struct commit *p = parent->item;
}
}
- ---static void compress_list(struct commit_list *list)
- ---{
- --- while (list) {
- --- struct commit *commit = list->item;
- --- struct commit_list *parent = commit->parents;
- --- list = list->next;
- ---
- --- if (!parent) {
- --- if (!same_tree_as_empty(commit->tree))
- --- commit->object.flags |= TREECHANGE;
- --- continue;
- --- }
- ---
- --- /*
- --- * Exactly one parent? Check if it leaves the tree
- --- * unchanged
- --- */
- --- if (!parent->next) {
- --- struct tree *t1 = commit->tree;
- --- struct tree *t2 = parent->item->tree;
- --- if (!t1 || !t2 || same_tree(t1, t2))
- --- continue;
- --- }
- --- commit->object.flags |= TREECHANGE;
- --- }
- ---}
- ---
static struct commit_list *limit_list(struct commit_list *list)
{
struct commit_list *newlist = NULL;
}
if (tree_objects)
mark_edges_uninteresting(newlist);
- --- if (paths && dense)
- --- compress_list(newlist);
if (bisect_list)
newlist = find_bisection(newlist);
return newlist;
dense = 0;
continue;
}
+ +++ if (!strcmp(arg, "--remove-empty")) {
+ +++ remove_empty_trees = 1;
+ +++ continue;
+ +++ }
if (!strcmp(arg, "--")) {
i++;
break;
#define REVERSED 1
static int show_type = NORMAL;
static int symbolic = 0;
++++ static int abbrev = 0;
static int output_sq = 0;
static int revs_count = 0;
putchar('^');
if (symbolic && name)
show(name);
++++ else if (abbrev)
++++ show(find_unique_abbrev(sha1, abbrev));
else
show(sha1_to_hex(sha1));
}
verify = 1;
continue;
}
++++ if (!strcmp(arg, "--short") ||
++++ !strncmp(arg, "--short=", 9)) {
++++ filter &= ~(DO_FLAGS|DO_NOREV);
++++ verify = 1;
++++ abbrev = DEFAULT_ABBREV;
++++ if (arg[8] == '=')
++++ abbrev = strtoul(arg + 9, NULL, 10);
++++ if (abbrev < MINIMUM_ABBREV)
++++ abbrev = MINIMUM_ABBREV;
++++ else if (40 <= abbrev)
++++ abbrev = 40;
++++ continue;
++++ }
if (!strcmp(arg, "--sq")) {
output_sq = 1;
continue;
}
if (verify)
die("Needed a single revision");
--- - if (lstat(arg, &st) < 0)
+++ + if ((filter & DO_REVS) &&
+++ + (filter & DO_NONFLAGS) && /* !def && */
+++ + lstat(arg, &st) < 0)
die("'%s': %s", arg, strerror(errno));
as_is = 1;
show_file(arg);