]> git.ipfire.org Git - thirdparty/git.git/blobdiff - log-tree.c
environment.h: move declarations for environment.c functions from cache.h
[thirdparty/git.git] / log-tree.c
index d3e7a40b648c7dc6eb30880ac3e04908342a8b09..e2bf8d6df75da73172c7e95c279b6b10da82aa74 100644 (file)
@@ -1,14 +1,20 @@
 #include "cache.h"
+#include "commit-reach.h"
 #include "config.h"
 #include "diff.h"
+#include "environment.h"
+#include "hex.h"
 #include "object-store.h"
 #include "repository.h"
+#include "tmp-objdir.h"
 #include "commit.h"
 #include "tag.h"
 #include "graph.h"
 #include "log-tree.h"
+#include "merge-ort.h"
 #include "reflog-walk.h"
 #include "refs.h"
+#include "replace-object.h"
 #include "string-list.h"
 #include "color.h"
 #include "gpg-interface.h"
@@ -16,6 +22,7 @@
 #include "line-log.h"
 #include "help.h"
 #include "range-diff.h"
+#include "strmap.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -84,7 +91,7 @@ static int match_ref_pattern(const char *refname,
                             const struct string_list_item *item)
 {
        int matched = 0;
-       if (item->util == NULL) {
+       if (!item->util) {
                if (!wildmatch(item->string, refname, 0))
                        matched = 1;
        } else {
@@ -131,12 +138,15 @@ static int ref_filter_match(const char *refname,
 }
 
 static int add_ref_decoration(const char *refname, const struct object_id *oid,
-                             int flags, void *cb_data)
+                             int flags UNUSED,
+                             void *cb_data)
 {
+       int i;
        struct object *obj;
        enum object_type objtype;
        enum decoration_type deco_type = DECORATION_NONE;
        struct decoration_filter *filter = (struct decoration_filter *)cb_data;
+       const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
 
        if (filter && !ref_filter_match(refname, filter))
                return 0;
@@ -161,16 +171,21 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
                return 0;
        obj = lookup_object_by_type(the_repository, oid, objtype);
 
-       if (starts_with(refname, "refs/heads/"))
-               deco_type = DECORATION_REF_LOCAL;
-       else if (starts_with(refname, "refs/remotes/"))
-               deco_type = DECORATION_REF_REMOTE;
-       else if (starts_with(refname, "refs/tags/"))
-               deco_type = DECORATION_REF_TAG;
-       else if (!strcmp(refname, "refs/stash"))
-               deco_type = DECORATION_REF_STASH;
-       else if (!strcmp(refname, "HEAD"))
-               deco_type = DECORATION_REF_HEAD;
+       for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
+               struct ref_namespace_info *info = &ref_namespace[i];
+
+               if (!info->decoration)
+                       continue;
+               if (info->exact) {
+                       if (!strcmp(refname, info->ref)) {
+                               deco_type = info->decoration;
+                               break;
+                       }
+               } else if (starts_with(refname, info->ref)) {
+                       deco_type = info->decoration;
+                       break;
+               }
+       }
 
        add_name_decoration(deco_type, refname, obj);
        while (obj->type == OBJ_TAG) {
@@ -184,7 +199,8 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
        return 0;
 }
 
-static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
+static int add_graft_decoration(const struct commit_graft *graft,
+                               void *cb_data UNUSED)
 {
        struct commit *commit = lookup_commit(the_repository, &graft->oid);
        if (!commit)
@@ -561,7 +577,7 @@ static int show_one_mergetag(struct commit *commit,
        struct strbuf signature = STRBUF_INIT;
 
        hash_object_file(the_hash_algo, extra->value, extra->len,
-                        type_name(OBJ_TAG), &oid);
+                        OBJ_TAG, &oid);
        tag = lookup_tag(the_repository, &oid);
        if (!tag)
                return -1; /* error message already given */
@@ -849,7 +865,7 @@ int log_tree_diff_flush(struct rev_info *opt)
        opt->shown_dashes = 0;
        diffcore_std(&opt->diffopt);
 
-       if (diff_queue_is_empty()) {
+       if (diff_queue_is_empty(&opt->diffopt)) {
                int saved_fmt = opt->diffopt.output_format;
                opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
                diff_flush(&opt->diffopt);
@@ -904,6 +920,105 @@ static int do_diff_combined(struct rev_info *opt, struct commit *commit)
        return !opt->loginfo;
 }
 
+static void setup_additional_headers(struct diff_options *o,
+                                    struct strmap *all_headers)
+{
+       struct hashmap_iter iter;
+       struct strmap_entry *entry;
+
+       /*
+        * Make o->additional_path_headers contain the subset of all_headers
+        * that match o->pathspec.  If there aren't any that match o->pathspec,
+        * then make o->additional_path_headers be NULL.
+        */
+
+       if (!o->pathspec.nr) {
+               o->additional_path_headers = all_headers;
+               return;
+       }
+
+       o->additional_path_headers = xmalloc(sizeof(struct strmap));
+       strmap_init_with_options(o->additional_path_headers, NULL, 0);
+       strmap_for_each_entry(all_headers, &iter, entry) {
+               if (match_pathspec(the_repository->index, &o->pathspec,
+                                  entry->key, strlen(entry->key),
+                                  0 /* prefix */, NULL /* seen */,
+                                  0 /* is_dir */))
+                       strmap_put(o->additional_path_headers,
+                                  entry->key, entry->value);
+       }
+       if (!strmap_get_size(o->additional_path_headers)) {
+               strmap_clear(o->additional_path_headers, 0);
+               FREE_AND_NULL(o->additional_path_headers);
+       }
+}
+
+static void cleanup_additional_headers(struct diff_options *o)
+{
+       if (!o->pathspec.nr) {
+               o->additional_path_headers = NULL;
+               return;
+       }
+       if (!o->additional_path_headers)
+               return;
+
+       strmap_clear(o->additional_path_headers, 0);
+       FREE_AND_NULL(o->additional_path_headers);
+}
+
+static int do_remerge_diff(struct rev_info *opt,
+                          struct commit_list *parents,
+                          struct object_id *oid)
+{
+       struct merge_options o;
+       struct commit_list *bases;
+       struct merge_result res = {0};
+       struct pretty_print_context ctx = {0};
+       struct commit *parent1 = parents->item;
+       struct commit *parent2 = parents->next->item;
+       struct strbuf parent1_desc = STRBUF_INIT;
+       struct strbuf parent2_desc = STRBUF_INIT;
+
+       /* Setup merge options */
+       init_merge_options(&o, the_repository);
+       o.show_rename_progress = 0;
+       o.record_conflict_msgs_as_headers = 1;
+       o.msg_header_prefix = "remerge";
+
+       ctx.abbrev = DEFAULT_ABBREV;
+       format_commit_message(parent1, "%h (%s)", &parent1_desc, &ctx);
+       format_commit_message(parent2, "%h (%s)", &parent2_desc, &ctx);
+       o.branch1 = parent1_desc.buf;
+       o.branch2 = parent2_desc.buf;
+
+       /* Parse the relevant commits and get the merge bases */
+       parse_commit_or_die(parent1);
+       parse_commit_or_die(parent2);
+       bases = get_merge_bases(parent1, parent2);
+
+       /* Re-merge the parents */
+       merge_incore_recursive(&o, bases, parent1, parent2, &res);
+
+       /* Show the diff */
+       setup_additional_headers(&opt->diffopt, res.path_messages);
+       diff_tree_oid(&res.tree->object.oid, oid, "", &opt->diffopt);
+       log_tree_diff_flush(opt);
+
+       /* Cleanup */
+       cleanup_additional_headers(&opt->diffopt);
+       strbuf_release(&parent1_desc);
+       strbuf_release(&parent2_desc);
+       merge_finalize(&o, &res);
+
+       /* Clean up the contents of the temporary object directory */
+       if (opt->remerge_objdir)
+               tmp_objdir_discard_objects(opt->remerge_objdir);
+       else
+               BUG("did a remerge diff without remerge_objdir?!?");
+
+       return !opt->loginfo;
+}
+
 /*
  * Show the diff of a commit.
  *
@@ -938,6 +1053,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        }
 
        if (is_merge) {
+               int octopus = (parents->next->next != NULL);
+
+               if (opt->remerge_diff) {
+                       if (octopus) {
+                               show_log(opt);
+                               fprintf(opt->diffopt.file,
+                                       "diff: warning: Skipping remerge-diff "
+                                       "for octopus merges.\n");
+                               return 1;
+                       }
+                       return do_remerge_diff(opt, parents, oid);
+               }
                if (opt->combine_merges)
                        return do_diff_combined(opt, commit);
                if (opt->separate_merges) {
@@ -982,6 +1109,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
        opt->loginfo = &log;
        opt->diffopt.no_free = 1;
 
+       /* NEEDSWORK: no restoring of no_free?  Why? */
        if (opt->line_level_traverse)
                return line_log_print(opt, commit);