]> git.ipfire.org Git - thirdparty/git.git/blobdiff - commit.c
config: do not leak excludes_file
[thirdparty/git.git] / commit.c
index 143f472c0f24bfeece3209a4c8bf70be9494b557..e433c33bb01fa6e3688a0b163c44d62f204d62d0 100644 (file)
--- a/commit.c
+++ b/commit.c
 #include "commit-reach.h"
 #include "run-command.h"
 #include "shallow.h"
+#include "hook.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
 int save_commit_buffer = 1;
+int no_graft_file_deprecated_advice;
 
 const char *commit_type = "commit";
 
@@ -57,6 +59,14 @@ struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref
        return c;
 }
 
+struct commit *lookup_commit_object(struct repository *r,
+                                   const struct object_id *oid)
+{
+       struct object *obj = parse_object(r, oid);
+       return obj ? object_as_type(obj, OBJ_COMMIT, 0) : NULL;
+
+}
+
 struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
 {
        struct object *obj = lookup_object(r, oid);
@@ -118,6 +128,17 @@ int commit_graft_pos(struct repository *r, const struct object_id *oid)
                       commit_graft_oid_access);
 }
 
+static void unparse_commit(struct repository *r, const struct object_id *oid)
+{
+       struct commit *c = lookup_commit(r, oid);
+
+       if (!c->object.parsed)
+               return;
+       free_commit_list(c->parents);
+       c->parents = NULL;
+       c->object.parsed = 0;
+}
+
 int register_commit_graft(struct repository *r, struct commit_graft *graft,
                          int ignore_dups)
 {
@@ -143,6 +164,7 @@ int register_commit_graft(struct repository *r, struct commit_graft *graft,
                        (r->parsed_objects->grafts_nr - pos - 1) *
                        sizeof(*r->parsed_objects->grafts));
        r->parsed_objects->grafts[pos] = graft;
+       unparse_commit(r, &graft->oid);
        return 0;
 }
 
@@ -190,7 +212,8 @@ static int read_graft_file(struct repository *r, const char *graft_file)
        struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
-       if (advice_graft_file_deprecated)
+       if (!no_graft_file_deprecated_advice &&
+           advice_enabled(ADVICE_GRAFT_FILE_DEPRECATED))
                advise(_("Support for <GIT_DIR>/info/grafts is deprecated\n"
                         "and will be removed in a future Git version.\n"
                         "\n"
@@ -246,6 +269,18 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
        return ret;
 }
 
+void reset_commit_grafts(struct repository *r)
+{
+       int i;
+
+       for (i = 0; i < r->parsed_objects->grafts_nr; i++) {
+               unparse_commit(r, &r->parsed_objects->grafts[i]->oid);
+               free(r->parsed_objects->grafts[i]);
+       }
+       r->parsed_objects->grafts_nr = 0;
+       r->parsed_objects->commit_graft_prepared = 0;
+}
+
 struct commit_buffer {
        void *buffer;
        unsigned long size;
@@ -394,17 +429,14 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
 
        if (item->object.parsed)
                return 0;
-
-       if (item->parents) {
-               /*
-                * Presumably this is leftover from an earlier failed parse;
-                * clear it out in preparation for us re-parsing (we'll hit the
-                * same error, but that's good, since it lets our caller know
-                * the result cannot be trusted.
-                */
-               free_commit_list(item->parents);
-               item->parents = NULL;
-       }
+       /*
+        * Presumably this is leftover from an earlier failed parse;
+        * clear it out in preparation for us re-parsing (we'll hit the
+        * same error, but that's good, since it lets our caller know
+        * the result cannot be trusted.
+        */
+       free_commit_list(item->parents);
+       item->parents = NULL;
 
        tail += size;
        if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
@@ -476,6 +508,17 @@ int repo_parse_commit_internal(struct repository *r,
        enum object_type type;
        void *buffer;
        unsigned long size;
+       struct object_info oi = {
+               .typep = &type,
+               .sizep = &size,
+               .contentp = &buffer,
+       };
+       /*
+        * Git does not support partial clones that exclude commits, so set
+        * OBJECT_INFO_SKIP_FETCH_OBJECT to fail fast when an object is missing.
+        */
+       int flags = OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_SKIP_FETCH_OBJECT |
+               OBJECT_INFO_DIE_IF_CORRUPT;
        int ret;
 
        if (!item)
@@ -484,8 +527,8 @@ int repo_parse_commit_internal(struct repository *r,
                return 0;
        if (use_commit_graph && parse_commit_in_graph(r, item))
                return 0;
-       buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
-       if (!buffer)
+
+       if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
                             oid_to_hex(&item->object.oid));
@@ -618,10 +661,11 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
        return commit_list_insert(item, pp);
 }
 
-static int commit_list_compare_by_date(const void *a, const void *b)
+static int commit_list_compare_by_date(const struct commit_list *a,
+                                      const struct commit_list *b)
 {
-       timestamp_t a_date = ((const struct commit_list *)a)->item->date;
-       timestamp_t b_date = ((const struct commit_list *)b)->item->date;
+       timestamp_t a_date = a->item->date;
+       timestamp_t b_date = b->item->date;
        if (a_date < b_date)
                return 1;
        if (a_date > b_date)
@@ -629,20 +673,11 @@ static int commit_list_compare_by_date(const void *a, const void *b)
        return 0;
 }
 
-static void *commit_list_get_next(const void *a)
-{
-       return ((const struct commit_list *)a)->next;
-}
-
-static void commit_list_set_next(void *a, void *next)
-{
-       ((struct commit_list *)a)->next = next;
-}
+DEFINE_LIST_SORT(static, commit_list_sort, struct commit_list, next);
 
 void commit_list_sort_by_date(struct commit_list **list)
 {
-       *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
-                               commit_list_compare_by_date);
+       commit_list_sort(list, commit_list_compare_by_date);
 }
 
 struct commit *pop_most_recent_commit(struct commit_list **list,
@@ -677,8 +712,10 @@ static void clear_commit_marks_1(struct commit_list **plist,
                if (!parents)
                        return;
 
-               while ((parents = parents->next))
-                       commit_list_insert(parents->item, plist);
+               while ((parents = parents->next)) {
+                       if (parents->item->object.flags & mark)
+                               commit_list_insert(parents->item, plist);
+               }
 
                commit = commit->parents->item;
        }
@@ -935,8 +972,9 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-                                 const char *ident, timestamp_t timestamp,
-                                 int tz, const char *message, void *cbdata)
+                                 const char *ident UNUSED,
+                                 timestamp_t timestamp UNUSED, int tz UNUSED,
+                                 const char *message UNUSED, void *cbdata)
 {
        struct rev_collect *revs = cbdata;
 
@@ -995,6 +1033,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        ret = bases->item;
 
 cleanup_return:
+       free(revs.commit);
        free_commit_list(bases);
        free(full_refname);
        return ret;
@@ -1210,8 +1249,10 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
 
        if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
-       ret = check_signature(payload.buf, payload.len, signature.buf,
-               signature.len, sigc);
+
+       sigc->payload_type = SIGNATURE_PAYLOAD_COMMIT;
+       sigc->payload = strbuf_detach(&payload, &sigc->payload_len);
+       ret = check_signature(sigc, signature.buf, signature.len);
 
  out:
        strbuf_release(&payload);
@@ -1500,7 +1541,7 @@ static int verify_utf8(struct strbuf *buf)
 static const char commit_utf8_warn[] =
 N_("Warning: commit message did not conform to UTF-8.\n"
    "You may want to amend it after fixing the message, or set the config\n"
-   "variable i18n.commitencoding to the encoding your project uses.\n");
+   "variable i18n.commitEncoding to the encoding your project uses.\n");
 
 int commit_tree_extended(const char *msg, size_t msg_len,
                         const struct object_id *tree,
@@ -1563,7 +1604,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
                goto out;
        }
 
-       result = write_object_file(buffer.buf, buffer.len, commit_type, ret);
+       result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
 out:
        strbuf_release(&buffer);
        return result;
@@ -1627,12 +1668,20 @@ struct commit_list **commit_list_append(struct commit *commit,
        return &new_commit->next;
 }
 
-const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+const char *find_header_mem(const char *msg, size_t len,
+                       const char *key, size_t *out_len)
 {
        int key_len = strlen(key);
        const char *line = msg;
 
-       while (line) {
+       /*
+        * NEEDSWORK: It's possible for strchrnul() to scan beyond the range
+        * given by len. However, current callers are safe because they compute
+        * len by scanning a NUL-terminated block of memory starting at msg.
+        * Nonetheless, it would be better to ensure the function does not look
+        * at msg beyond the len provided by the caller.
+        */
+       while (line && line < msg + len) {
                const char *eol = strchrnul(line, '\n');
 
                if (line == eol)
@@ -1649,6 +1698,10 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
        return NULL;
 }
 
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+{
+       return find_header_mem(msg, strlen(msg), key, out_len);
+}
 /*
  * Inspect the given string and determine the true "end" of the log message, in
  * order to find where to put a new Signed-off-by trailer.  Ignored are
@@ -1696,24 +1749,25 @@ size_t ignore_non_trailer(const char *buf, size_t len)
 }
 
 int run_commit_hook(int editor_is_used, const char *index_file,
-                   const char *name, ...)
+                   int *invoked_hook, const char *name, ...)
 {
-       struct strvec hook_env = STRVEC_INIT;
+       struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
        va_list args;
-       int ret;
+       const char *arg;
 
-       strvec_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+       strvec_pushf(&opt.env, "GIT_INDEX_FILE=%s", index_file);
 
        /*
         * Let the hook know that no editor will be launched.
         */
        if (!editor_is_used)
-               strvec_push(&hook_env, "GIT_EDITOR=:");
+               strvec_push(&opt.env, "GIT_EDITOR=:");
 
        va_start(args, name);
-       ret = run_hook_ve(hook_env.v, name, args);
+       while ((arg = va_arg(args, const char *)))
+               strvec_push(&opt.args, arg);
        va_end(args);
-       strvec_clear(&hook_env);
 
-       return ret;
+       opt.invoked_hook = invoked_hook;
+       return run_hooks_opt(name, &opt);
 }