patch-ids.con commit Merge branch 'jk/patch-ids-no-merges' (f0a84de)
   1#include "cache.h"
   2#include "diff.h"
   3#include "commit.h"
   4#include "sha1-lookup.h"
   5#include "patch-ids.h"
   6
   7static int patch_id_defined(struct commit *commit)
   8{
   9        /* must be 0 or 1 parents */
  10        return !commit->parents || !commit->parents->next;
  11}
  12
  13int commit_patch_id(struct commit *commit, struct diff_options *options,
  14                    unsigned char *sha1, int diff_header_only)
  15{
  16        if (!patch_id_defined(commit))
  17                return -1;
  18
  19        if (commit->parents)
  20                diff_tree_sha1(commit->parents->item->object.oid.hash,
  21                               commit->object.oid.hash, "", options);
  22        else
  23                diff_root_tree_sha1(commit->object.oid.hash, "", options);
  24        diffcore_std(options);
  25        return diff_flush_patch_id(options, sha1, diff_header_only);
  26}
  27
  28/*
  29 * When we cannot load the full patch-id for both commits for whatever
  30 * reason, the function returns -1 (i.e. return error(...)). Despite
  31 * the "cmp" in the name of this function, the caller only cares about
  32 * the return value being zero (a and b are equivalent) or non-zero (a
  33 * and b are different), and returning non-zero would keep both in the
  34 * result, even if they actually were equivalent, in order to err on
  35 * the side of safety.  The actual value being negative does not have
  36 * any significance; only that it is non-zero matters.
  37 */
  38static int patch_id_cmp(struct patch_id *a,
  39                        struct patch_id *b,
  40                        struct diff_options *opt)
  41{
  42        if (is_null_sha1(a->patch_id) &&
  43            commit_patch_id(a->commit, opt, a->patch_id, 0))
  44                return error("Could not get patch ID for %s",
  45                        oid_to_hex(&a->commit->object.oid));
  46        if (is_null_sha1(b->patch_id) &&
  47            commit_patch_id(b->commit, opt, b->patch_id, 0))
  48                return error("Could not get patch ID for %s",
  49                        oid_to_hex(&b->commit->object.oid));
  50        return hashcmp(a->patch_id, b->patch_id);
  51}
  52
  53int init_patch_ids(struct patch_ids *ids)
  54{
  55        memset(ids, 0, sizeof(*ids));
  56        diff_setup(&ids->diffopts);
  57        ids->diffopts.detect_rename = 0;
  58        DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
  59        diff_setup_done(&ids->diffopts);
  60        hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, 256);
  61        return 0;
  62}
  63
  64int free_patch_ids(struct patch_ids *ids)
  65{
  66        hashmap_free(&ids->patches, 1);
  67        return 0;
  68}
  69
  70static int init_patch_id_entry(struct patch_id *patch,
  71                               struct commit *commit,
  72                               struct patch_ids *ids)
  73{
  74        unsigned char header_only_patch_id[GIT_SHA1_RAWSZ];
  75
  76        patch->commit = commit;
  77        if (commit_patch_id(commit, &ids->diffopts, header_only_patch_id, 1))
  78                return -1;
  79
  80        hashmap_entry_init(patch, sha1hash(header_only_patch_id));
  81        return 0;
  82}
  83
  84struct patch_id *has_commit_patch_id(struct commit *commit,
  85                                     struct patch_ids *ids)
  86{
  87        struct patch_id patch;
  88
  89        if (!patch_id_defined(commit))
  90                return NULL;
  91
  92        memset(&patch, 0, sizeof(patch));
  93        if (init_patch_id_entry(&patch, commit, ids))
  94                return NULL;
  95
  96        return hashmap_get(&ids->patches, &patch, &ids->diffopts);
  97}
  98
  99struct patch_id *add_commit_patch_id(struct commit *commit,
 100                                     struct patch_ids *ids)
 101{
 102        struct patch_id *key = xcalloc(1, sizeof(*key));
 103
 104        if (!patch_id_defined(commit))
 105                return NULL;
 106
 107        if (init_patch_id_entry(key, commit, ids)) {
 108                free(key);
 109                return NULL;
 110        }
 111
 112        hashmap_add(&ids->patches, key);
 113        return key;
 114}