summaryrefslogtreecommitdiff
path: root/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'commit.c')
-rw-r--r--commit.c145
1 files changed, 112 insertions, 33 deletions
diff --git a/commit.c b/commit.c
index 89b8efc611..0fb9316931 100644
--- a/commit.c
+++ b/commit.c
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
#include "commit-graph.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
+#include "object-name.h"
#include "object-store.h"
#include "pkt-line.h"
#include "utf8.h"
@@ -20,7 +24,9 @@
#include "refs.h"
#include "commit-reach.h"
#include "run-command.h"
+#include "setup.h"
#include "shallow.h"
+#include "tree.h"
#include "hook.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -59,6 +65,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);
@@ -72,10 +86,10 @@ struct commit *lookup_commit_reference_by_name(const char *name)
struct object_id oid;
struct commit *commit;
- if (get_oid_committish(name, &oid))
+ if (repo_get_oid_committish(the_repository, name, &oid))
return NULL;
commit = lookup_commit_reference(the_repository, &oid);
- if (parse_commit(commit))
+ if (repo_parse_commit(the_repository, commit))
return NULL;
return commit;
}
@@ -83,6 +97,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
static timestamp_t parse_commit_date(const char *buf, const char *tail)
{
const char *dateptr;
+ const char *eol;
if (buf + 6 >= tail)
return 0;
@@ -94,16 +109,56 @@ static timestamp_t parse_commit_date(const char *buf, const char *tail)
return 0;
if (memcmp(buf, "committer", 9))
return 0;
- while (buf < tail && *buf++ != '>')
- /* nada */;
- if (buf >= tail)
+
+ /*
+ * Jump to end-of-line so that we can walk backwards to find the
+ * end-of-email ">". This is more forgiving of malformed cases
+ * because unexpected characters tend to be in the name and email
+ * fields.
+ */
+ eol = memchr(buf, '\n', tail - buf);
+ if (!eol)
return 0;
- dateptr = buf;
- while (buf < tail && *buf++ != '\n')
- /* nada */;
- if (buf >= tail)
+ dateptr = eol;
+ while (dateptr > buf && dateptr[-1] != '>')
+ dateptr--;
+ if (dateptr == buf)
+ return 0;
+
+ /*
+ * Trim leading whitespace, but make sure we have at least one
+ * non-whitespace character, as parse_timestamp() will otherwise walk
+ * right past the newline we found in "eol" when skipping whitespace
+ * itself.
+ *
+ * In theory it would be sufficient to allow any character not matched
+ * by isspace(), but there's a catch: our isspace() does not
+ * necessarily match the behavior of parse_timestamp(), as the latter
+ * is implemented by system routines which match more exotic control
+ * codes, or even locale-dependent sequences.
+ *
+ * Since we expect the timestamp to be a number, we can check for that.
+ * Anything else (e.g., a non-numeric token like "foo") would just
+ * cause parse_timestamp() to return 0 anyway.
+ */
+ while (dateptr < eol && isspace(*dateptr))
+ dateptr++;
+ if (!isdigit(*dateptr) && *dateptr != '-')
return 0;
- /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+
+ /*
+ * We know there is at least one digit (or dash), so we'll begin
+ * parsing there and stop at worst case at eol.
+ *
+ * Note that we may feed parse_timestamp() extra characters here if the
+ * commit is malformed, and it will parse as far as it can. For
+ * example, "123foo456" would return "123". That might be questionable
+ * (versus returning "0"), but it would help in a hypothetical case
+ * like "123456+0100", where the whitespace from the timezone is
+ * missing. Since such syntactic errors may be baked into history and
+ * hard to correct now, let's err on trying to make our best guess
+ * here, rather than insist on perfect syntax.
+ */
return parse_timestamp(dateptr, NULL, 10);
}
@@ -374,7 +429,7 @@ struct tree *repo_get_commit_tree(struct repository *r,
struct object_id *get_commit_tree_oid(const struct commit *commit)
{
- struct tree *tree = get_commit_tree(commit);
+ struct tree *tree = repo_get_commit_tree(the_repository, commit);
return tree ? &tree->object.oid : NULL;
}
@@ -500,6 +555,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)
@@ -508,8 +574,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));
@@ -536,7 +602,7 @@ int repo_parse_commit_gently(struct repository *r,
void parse_commit_or_die(struct commit *item)
{
- if (parse_commit(item))
+ if (repo_parse_commit(the_repository, item))
die("unable to parse commit %s",
item ? oid_to_hex(&item->object.oid) : "(null)");
}
@@ -669,7 +735,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
while (parents) {
struct commit *commit = parents->item;
- if (!parse_commit(commit) && !(commit->object.flags & mark)) {
+ if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) {
commit->object.flags |= mark;
commit_list_insert_by_date(commit, list);
}
@@ -693,8 +759,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;
}
@@ -741,7 +809,8 @@ define_commit_slab(author_date_slab, timestamp_t);
void record_author_date(struct author_date_slab *author_date,
struct commit *commit)
{
- const char *buffer = get_commit_buffer(commit, NULL);
+ const char *buffer = repo_get_commit_buffer(the_repository, commit,
+ NULL);
struct ident_split ident;
const char *ident_line;
size_t ident_len;
@@ -761,7 +830,7 @@ void record_author_date(struct author_date_slab *author_date,
*(author_date_slab_at(author_date, commit)) = date;
fail_exit:
- unuse_commit_buffer(commit, buffer);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
}
int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -780,7 +849,8 @@ int compare_commits_by_author_date(const void *a_, const void *b_,
return 0;
}
-int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_,
+ void *unused UNUSED)
{
const struct commit *a = a_, *b = b_;
const timestamp_t generation_a = commit_graph_generation(a),
@@ -800,7 +870,8 @@ int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void
return 0;
}
-int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_commit_date(const void *a_, const void *b_,
+ void *unused UNUSED)
{
const struct commit *a = a_, *b = b_;
/* newer commits with larger date first */
@@ -942,7 +1013,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
commit = lookup_commit(the_repository, oid);
if (!commit ||
(commit->object.flags & TMP_MARK) ||
- parse_commit(commit))
+ repo_parse_commit(the_repository, commit))
return;
ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
@@ -974,7 +1045,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
struct commit *ret = NULL;
char *full_refname;
- switch (dwim_ref(refname, strlen(refname), &oid, &full_refname, 0)) {
+ switch (repo_dwim_ref(the_repository, refname, strlen(refname), &oid,
+ &full_refname, 0)) {
case 0:
die("No such ref: '%s'", refname);
case 1:
@@ -993,7 +1065,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
for (i = 0; i < revs.nr; i++)
revs.commit[i]->object.flags &= ~TMP_MARK;
- bases = get_merge_bases_many(commit, revs.nr, revs.commit);
+ bases = repo_get_merge_bases_many(the_repository, commit, revs.nr,
+ revs.commit);
/*
* There should be one and only one merge base, when we found
@@ -1012,6 +1085,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;
@@ -1073,10 +1147,11 @@ int parse_signed_commit(const struct commit *commit,
const struct git_hash_algo *algop)
{
unsigned long size;
- const char *buffer = get_commit_buffer(commit, &size);
+ const char *buffer = repo_get_commit_buffer(the_repository, commit,
+ &size);
int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
- unuse_commit_buffer(commit, buffer);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
return ret;
}
@@ -1187,7 +1262,8 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
desc = merge_remote_util(parent);
if (!desc || !desc->obj)
return;
- buf = read_object_file(&desc->obj->oid, &type, &size);
+ buf = repo_read_object_file(the_repository, &desc->obj->oid, &type,
+ &size);
if (!buf || type != OBJ_TAG)
goto free_return;
if (!parse_signature(buf, size, &payload, &signature))
@@ -1249,7 +1325,8 @@ void verify_merge_signature(struct commit *commit, int verbosity,
ret = check_commit_signature(commit, &signature_check);
- find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
+ repo_find_unique_abbrev_r(the_repository, hex, &commit->object.oid,
+ DEFAULT_ABBREV);
switch (signature_check.result) {
case 'G':
if (ret || (check_trust && signature_check.trust_level < TRUST_MARGINAL))
@@ -1294,9 +1371,10 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
{
struct commit_extra_header *extra = NULL;
unsigned long size;
- const char *buffer = get_commit_buffer(commit, &size);
+ const char *buffer = repo_get_commit_buffer(the_repository, commit,
+ &size);
extra = read_commit_extra_header_lines(buffer, size, exclude);
- unuse_commit_buffer(commit, buffer);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
return extra;
}
@@ -1610,10 +1688,11 @@ struct commit *get_merge_parent(const char *name)
struct object *obj;
struct commit *commit;
struct object_id oid;
- if (get_oid(name, &oid))
+ if (repo_get_oid(the_repository, name, &oid))
return NULL;
obj = parse_object(the_repository, &oid);
- commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+ commit = (struct commit *)repo_peel_to_type(the_repository, name, 0,
+ obj, OBJ_COMMIT);
if (commit && !merge_remote_util(commit))
set_merge_remote_desc(commit, name, obj);
return commit;