2006-01-07 17:33:54 +08:00
|
|
|
#include "cache.h"
|
2005-05-19 07:14:22 +08:00
|
|
|
#include "tag.h"
|
2005-04-19 02:39:48 +08:00
|
|
|
#include "commit.h"
|
2018-04-10 20:56:05 +08:00
|
|
|
#include "commit-graph.h"
|
2018-05-16 07:42:16 +08:00
|
|
|
#include "repository.h"
|
2018-05-16 07:42:15 +08:00
|
|
|
#include "object-store.h"
|
2006-10-31 03:09:06 +08:00
|
|
|
#include "pkt-line.h"
|
2006-12-26 03:48:35 +08:00
|
|
|
#include "utf8.h"
|
2007-04-09 17:34:05 +08:00
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
2009-10-09 18:21:57 +08:00
|
|
|
#include "notes.h"
|
2018-05-16 05:48:42 +08:00
|
|
|
#include "alloc.h"
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
#include "gpg-interface.h"
|
2012-04-01 06:10:39 +08:00
|
|
|
#include "mergesort.h"
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
#include "commit-slab.h"
|
2013-06-07 12:58:12 +08:00
|
|
|
#include "prio-queue.h"
|
2014-02-27 02:49:22 +08:00
|
|
|
#include "sha1-lookup.h"
|
interpret-trailers: honor the cut line
If a commit message is edited with the "verbose" option, the buffer
will have a cut line and diff after the log message, like so:
my subject
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/foo.txt b/foo.txt
index 5716ca5..7601807 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1 @@
-bar
+baz
"git interpret-trailers" is unaware of the cut line, and assumes the
trailer block would be at the end of the whole thing. This can easily
be seen with:
$ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \
git commit --amend -v
Teach "git interpret-trailers" to notice the cut-line and ignore the
remainder of the input when looking for a place to add new trailer
block. This makes it consistent with how "git commit -v -s" inserts a
new Signed-off-by: line.
This can be done by the same logic as the existing helper function,
wt_status_truncate_message_at_cut_line(), uses, but it wants the caller
to pass a strbuf to it. Because the function ignore_non_trailer() used
by the command takes a <pointer, length> pair, not a strbuf, steal the
logic from wt_status_truncate_message_at_cut_line() to create a new
wt_status_locate_end() helper function that takes <pointer, length>
pair, and make ignore_non_trailer() call it to help "interpret-trailers".
Since there is only one caller of wt_status_truncate_message_at_cut_line()
in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead
and remove the old helper that no longer has any caller.
Signed-off-by: Brian Malehorn <bmalehorn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-16 14:06:49 +08:00
|
|
|
#include "wt-status.h"
|
Deprecate support for .git/info/grafts
The grafts feature was a convenient way to "stitch together" ancient
history to the fresh start of linux.git.
Its implementation is, however, not up to Git's standards, as there are
too many ways where it can lead to surprising and unwelcome behavior.
For example, when pushing from a repository with active grafts, it is
possible to miss commits that have been "grafted out", resulting in a
broken state on the other side.
Also, the grafts feature is limited to "rewriting" commits' list of
parents, it cannot replace anything else.
The much younger feature implemented as `git replace` set out to remedy
those limitations and dangerous bugs.
Seeing as `git replace` is pretty mature by now (since 4228e8bc98
(replace: add --graft option, 2014-07-19) it can perform the graft
file's duties), it is time to deprecate support for the graft file, and
to retire it eventually.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Reviewed-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-29 06:44:44 +08:00
|
|
|
#include "advice.h"
|
2005-04-19 02:39:48 +08:00
|
|
|
|
2012-09-16 04:58:15 +08:00
|
|
|
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
|
|
|
|
|
[PATCH] Avoid wasting memory in git-rev-list
As pointed out on the list, git-rev-list can use a lot of memory.
One low-hanging fruit is to free the commit buffer for commits that we
parse. By default, parse_commit() will save away the buffer, since a lot
of cases do want it, and re-reading it continually would be unnecessary.
However, in many cases the buffer isn't actually necessary and saving it
just wastes memory.
We could just free the buffer ourselves, but especially in git-rev-list,
we actually end up using the helper functions that automatically add
parent commits to the commit lists, so we don't actually control the
commit parsing directly.
Instead, just make this behaviour of "parse_commit()" a global flag.
Maybe this is a bit tasteless, but it's very simple, and it makes a
noticable difference in memory usage.
Before the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.02system 0:00.28elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3714minor)pagefaults 0swaps
after the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.00system 0:00.27elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2433minor)pagefaults 0swaps
note how the minor faults have decreased from 3714 pages to 2433 pages.
That's all due to the fewer anonymous pages allocated to hold the comment
buffers and their metadata.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-09-16 05:43:17 +08:00
|
|
|
int save_commit_buffer = 1;
|
|
|
|
|
2005-04-19 02:39:48 +08:00
|
|
|
const char *commit_type = "commit";
|
|
|
|
|
2018-06-29 09:22:21 +08:00
|
|
|
struct commit *lookup_commit_reference_gently(struct repository *r,
|
2018-06-29 09:21:57 +08:00
|
|
|
const struct object_id *oid, int quiet)
|
2005-05-19 07:14:22 +08:00
|
|
|
{
|
2018-06-29 09:22:21 +08:00
|
|
|
struct object *obj = deref_tag(r,
|
|
|
|
parse_object(r, oid),
|
2018-06-29 09:21:51 +08:00
|
|
|
NULL, 0);
|
2005-05-19 07:14:22 +08:00
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return NULL;
|
2018-06-29 09:22:21 +08:00
|
|
|
return object_as_type(r, obj, OBJ_COMMIT, quiet);
|
2005-08-21 17:51:10 +08:00
|
|
|
}
|
|
|
|
|
2018-06-29 09:22:22 +08:00
|
|
|
struct commit *lookup_commit_reference(struct repository *r, const struct object_id *oid)
|
2005-08-21 17:51:10 +08:00
|
|
|
{
|
2018-06-29 09:22:22 +08:00
|
|
|
return lookup_commit_reference_gently(r, oid, 0);
|
2005-05-19 07:14:22 +08:00
|
|
|
}
|
|
|
|
|
Convert lookup_commit* to struct object_id
Convert lookup_commit, lookup_commit_or_die,
lookup_commit_reference, and lookup_commit_reference_gently to take
struct object_id arguments.
Introduce a temporary in parse_object buffer in order to convert this
function. This is required since in order to convert parse_object and
parse_object_buffer, lookup_commit_reference_gently and
lookup_commit_or_die would need to be converted. Not introducing a
temporary would therefore require that lookup_commit_or_die take a
struct object_id *, but lookup_commit would take unsigned char *,
leaving a confusing and hard-to-use interface.
parse_object_buffer will lose this temporary in a later patch.
This commit was created with manual changes to commit.c, commit.h, and
object.c, plus the following semantic patch:
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1.hash, E2)
+ lookup_commit_reference_gently(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1->hash, E2)
+ lookup_commit_reference_gently(E1, E2)
@@
expression E1;
@@
- lookup_commit_reference(E1.hash)
+ lookup_commit_reference(&E1)
@@
expression E1;
@@
- lookup_commit_reference(E1->hash)
+ lookup_commit_reference(E1)
@@
expression E1;
@@
- lookup_commit(E1.hash)
+ lookup_commit(&E1)
@@
expression E1;
@@
- lookup_commit(E1->hash)
+ lookup_commit(E1)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1.hash, E2)
+ lookup_commit_or_die(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1->hash, E2)
+ lookup_commit_or_die(E1, E2)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 06:10:10 +08:00
|
|
|
struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name)
|
2011-09-17 19:57:45 +08:00
|
|
|
{
|
2018-06-29 09:21:58 +08:00
|
|
|
struct commit *c = lookup_commit_reference(the_repository, oid);
|
2011-09-17 19:57:45 +08:00
|
|
|
if (!c)
|
|
|
|
die(_("could not parse %s"), ref_name);
|
Convert lookup_commit* to struct object_id
Convert lookup_commit, lookup_commit_or_die,
lookup_commit_reference, and lookup_commit_reference_gently to take
struct object_id arguments.
Introduce a temporary in parse_object buffer in order to convert this
function. This is required since in order to convert parse_object and
parse_object_buffer, lookup_commit_reference_gently and
lookup_commit_or_die would need to be converted. Not introducing a
temporary would therefore require that lookup_commit_or_die take a
struct object_id *, but lookup_commit would take unsigned char *,
leaving a confusing and hard-to-use interface.
parse_object_buffer will lose this temporary in a later patch.
This commit was created with manual changes to commit.c, commit.h, and
object.c, plus the following semantic patch:
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1.hash, E2)
+ lookup_commit_reference_gently(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1->hash, E2)
+ lookup_commit_reference_gently(E1, E2)
@@
expression E1;
@@
- lookup_commit_reference(E1.hash)
+ lookup_commit_reference(&E1)
@@
expression E1;
@@
- lookup_commit_reference(E1->hash)
+ lookup_commit_reference(E1)
@@
expression E1;
@@
- lookup_commit(E1.hash)
+ lookup_commit(&E1)
@@
expression E1;
@@
- lookup_commit(E1->hash)
+ lookup_commit(E1)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1.hash, E2)
+ lookup_commit_or_die(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1->hash, E2)
+ lookup_commit_or_die(E1, E2)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 06:10:10 +08:00
|
|
|
if (oidcmp(oid, &c->object.oid)) {
|
2011-09-17 19:57:45 +08:00
|
|
|
warning(_("%s %s is not a commit!"),
|
Convert lookup_commit* to struct object_id
Convert lookup_commit, lookup_commit_or_die,
lookup_commit_reference, and lookup_commit_reference_gently to take
struct object_id arguments.
Introduce a temporary in parse_object buffer in order to convert this
function. This is required since in order to convert parse_object and
parse_object_buffer, lookup_commit_reference_gently and
lookup_commit_or_die would need to be converted. Not introducing a
temporary would therefore require that lookup_commit_or_die take a
struct object_id *, but lookup_commit would take unsigned char *,
leaving a confusing and hard-to-use interface.
parse_object_buffer will lose this temporary in a later patch.
This commit was created with manual changes to commit.c, commit.h, and
object.c, plus the following semantic patch:
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1.hash, E2)
+ lookup_commit_reference_gently(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1->hash, E2)
+ lookup_commit_reference_gently(E1, E2)
@@
expression E1;
@@
- lookup_commit_reference(E1.hash)
+ lookup_commit_reference(&E1)
@@
expression E1;
@@
- lookup_commit_reference(E1->hash)
+ lookup_commit_reference(E1)
@@
expression E1;
@@
- lookup_commit(E1.hash)
+ lookup_commit(&E1)
@@
expression E1;
@@
- lookup_commit(E1->hash)
+ lookup_commit(E1)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1.hash, E2)
+ lookup_commit_or_die(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1->hash, E2)
+ lookup_commit_or_die(E1, E2)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 06:10:10 +08:00
|
|
|
ref_name, oid_to_hex(oid));
|
2011-09-17 19:57:45 +08:00
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2018-06-29 09:22:10 +08:00
|
|
|
struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
|
2005-04-19 02:39:48 +08:00
|
|
|
{
|
2018-06-29 09:22:10 +08:00
|
|
|
struct object *obj = lookup_object(r, oid->hash);
|
2014-07-13 14:41:55 +08:00
|
|
|
if (!obj)
|
2018-06-29 09:22:10 +08:00
|
|
|
return create_object(r, oid->hash,
|
|
|
|
alloc_commit_node(r));
|
|
|
|
return object_as_type(r, obj, OBJ_COMMIT, 0);
|
2005-04-19 02:39:48 +08:00
|
|
|
}
|
|
|
|
|
2010-11-03 03:59:07 +08:00
|
|
|
struct commit *lookup_commit_reference_by_name(const char *name)
|
|
|
|
{
|
2015-03-14 07:39:34 +08:00
|
|
|
struct object_id oid;
|
2010-11-03 03:59:07 +08:00
|
|
|
struct commit *commit;
|
|
|
|
|
sha1_name: convert get_sha1* to get_oid*
Now that all the callers of get_sha1 directly or indirectly use struct
object_id, rename the functions starting with get_sha1 to start with
get_oid. Convert the internals in sha1_name.c to use struct object_id
as well, and eliminate explicit length checks where possible. Convert a
use of 40 in get_oid_basic to GIT_SHA1_HEXSZ.
Outside of sha1_name.c and cache.h, this transition was made with the
following semantic patch:
@@
expression E1, E2;
@@
- get_sha1(E1, E2.hash)
+ get_oid(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1(E1, E2->hash)
+ get_oid(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2.hash)
+ get_oid_committish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2->hash)
+ get_oid_committish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2.hash)
+ get_oid_treeish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2->hash)
+ get_oid_treeish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2.hash)
+ get_oid_commit(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2->hash)
+ get_oid_commit(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2.hash)
+ get_oid_tree(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2->hash)
+ get_oid_tree(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2.hash)
+ get_oid_blob(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2->hash)
+ get_oid_blob(E1, E2)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3.hash, E4)
+ get_oid_with_context(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3->hash, E4)
+ get_oid_with_context(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-14 07:49:28 +08:00
|
|
|
if (get_oid_committish(name, &oid))
|
2010-11-03 03:59:07 +08:00
|
|
|
return NULL;
|
2018-06-29 09:21:58 +08:00
|
|
|
commit = lookup_commit_reference(the_repository, &oid);
|
2013-10-24 16:53:19 +08:00
|
|
|
if (parse_commit(commit))
|
2010-11-03 03:59:07 +08:00
|
|
|
return NULL;
|
|
|
|
return commit;
|
|
|
|
}
|
|
|
|
|
2017-04-27 03:29:31 +08:00
|
|
|
static timestamp_t parse_commit_date(const char *buf, const char *tail)
|
2005-04-19 02:39:48 +08:00
|
|
|
{
|
2008-01-20 01:35:23 +08:00
|
|
|
const char *dateptr;
|
2005-04-19 02:39:48 +08:00
|
|
|
|
2008-01-20 01:35:23 +08:00
|
|
|
if (buf + 6 >= tail)
|
|
|
|
return 0;
|
2005-04-19 02:39:48 +08:00
|
|
|
if (memcmp(buf, "author", 6))
|
|
|
|
return 0;
|
2008-01-20 01:35:23 +08:00
|
|
|
while (buf < tail && *buf++ != '\n')
|
2005-04-19 02:39:48 +08:00
|
|
|
/* nada */;
|
2008-01-20 01:35:23 +08:00
|
|
|
if (buf + 9 >= tail)
|
|
|
|
return 0;
|
2005-04-19 02:39:48 +08:00
|
|
|
if (memcmp(buf, "committer", 9))
|
|
|
|
return 0;
|
2008-01-20 01:35:23 +08:00
|
|
|
while (buf < tail && *buf++ != '>')
|
2005-04-19 02:39:48 +08:00
|
|
|
/* nada */;
|
2008-01-20 01:35:23 +08:00
|
|
|
if (buf >= tail)
|
|
|
|
return 0;
|
|
|
|
dateptr = buf;
|
|
|
|
while (buf < tail && *buf++ != '\n')
|
|
|
|
/* nada */;
|
|
|
|
if (buf >= tail)
|
|
|
|
return 0;
|
2017-04-21 18:45:44 +08:00
|
|
|
/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
|
|
|
|
return parse_timestamp(dateptr, NULL, 10);
|
2005-04-19 02:39:48 +08:00
|
|
|
}
|
|
|
|
|
2014-02-27 02:49:22 +08:00
|
|
|
static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
|
|
|
|
{
|
|
|
|
struct commit_graft **commit_graft_table = table;
|
2015-03-14 07:39:34 +08:00
|
|
|
return commit_graft_table[index]->oid.hash;
|
2014-02-27 02:49:22 +08:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:51:47 +08:00
|
|
|
static int commit_graft_pos(struct repository *r, const unsigned char *sha1)
|
2005-07-30 15:58:28 +08:00
|
|
|
{
|
2018-05-18 06:51:47 +08:00
|
|
|
return sha1_pos(sha1, r->parsed_objects->grafts,
|
|
|
|
r->parsed_objects->grafts_nr,
|
2014-02-27 02:49:22 +08:00
|
|
|
commit_graft_sha1_access);
|
2005-07-30 15:58:28 +08:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:51:48 +08:00
|
|
|
int register_commit_graft(struct repository *r, struct commit_graft *graft,
|
|
|
|
int ignore_dups)
|
2006-04-07 14:58:51 +08:00
|
|
|
{
|
2018-05-18 06:51:48 +08:00
|
|
|
int pos = commit_graft_pos(r, graft->oid.hash);
|
2007-06-07 15:04:01 +08:00
|
|
|
|
2006-04-07 14:58:51 +08:00
|
|
|
if (0 <= pos) {
|
|
|
|
if (ignore_dups)
|
|
|
|
free(graft);
|
|
|
|
else {
|
2018-05-18 06:51:48 +08:00
|
|
|
free(r->parsed_objects->grafts[pos]);
|
|
|
|
r->parsed_objects->grafts[pos] = graft;
|
2006-04-07 14:58:51 +08:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
pos = -pos - 1;
|
2018-05-18 06:51:48 +08:00
|
|
|
ALLOC_GROW(r->parsed_objects->grafts,
|
|
|
|
r->parsed_objects->grafts_nr + 1,
|
|
|
|
r->parsed_objects->grafts_alloc);
|
|
|
|
r->parsed_objects->grafts_nr++;
|
|
|
|
if (pos < r->parsed_objects->grafts_nr)
|
|
|
|
memmove(r->parsed_objects->grafts + pos + 1,
|
|
|
|
r->parsed_objects->grafts + pos,
|
|
|
|
(r->parsed_objects->grafts_nr - pos - 1) *
|
|
|
|
sizeof(*r->parsed_objects->grafts));
|
|
|
|
r->parsed_objects->grafts[pos] = graft;
|
2006-04-07 14:58:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-19 02:33:12 +08:00
|
|
|
struct commit_graft *read_graft_line(struct strbuf *line)
|
2006-04-07 14:58:51 +08:00
|
|
|
{
|
|
|
|
/* The format is just "Commit Parent1 Parent2 ...\n" */
|
2017-08-19 02:33:14 +08:00
|
|
|
int i, phase;
|
|
|
|
const char *tail = NULL;
|
2006-04-07 14:58:51 +08:00
|
|
|
struct commit_graft *graft = NULL;
|
2017-08-19 02:33:14 +08:00
|
|
|
struct object_id dummy_oid, *oid;
|
2006-04-07 14:58:51 +08:00
|
|
|
|
2017-08-19 02:33:12 +08:00
|
|
|
strbuf_rtrim(line);
|
|
|
|
if (!line->len || line->buf[0] == '#')
|
2006-04-17 05:24:56 +08:00
|
|
|
return NULL;
|
2017-08-19 02:33:14 +08:00
|
|
|
/*
|
|
|
|
* phase 0 verifies line, counts hashes in line and allocates graft
|
|
|
|
* phase 1 fills graft
|
|
|
|
*/
|
|
|
|
for (phase = 0; phase < 2; phase++) {
|
|
|
|
oid = graft ? &graft->oid : &dummy_oid;
|
|
|
|
if (parse_oid_hex(line->buf, oid, &tail))
|
2006-04-07 14:58:51 +08:00
|
|
|
goto bad_graft_data;
|
2017-08-19 02:33:14 +08:00
|
|
|
for (i = 0; *tail != '\0'; i++) {
|
|
|
|
oid = graft ? &graft->parent[i] : &dummy_oid;
|
|
|
|
if (!isspace(*tail++) || parse_oid_hex(tail, oid, &tail))
|
|
|
|
goto bad_graft_data;
|
|
|
|
}
|
|
|
|
if (!graft) {
|
|
|
|
graft = xmalloc(st_add(sizeof(*graft),
|
|
|
|
st_mult(sizeof(struct object_id), i)));
|
|
|
|
graft->nr_parent = i;
|
|
|
|
}
|
2006-04-07 14:58:51 +08:00
|
|
|
}
|
|
|
|
return graft;
|
2010-12-02 03:15:59 +08:00
|
|
|
|
|
|
|
bad_graft_data:
|
2017-08-19 02:33:12 +08:00
|
|
|
error("bad graft data: %s", line->buf);
|
2017-08-19 02:33:14 +08:00
|
|
|
assert(!graft);
|
2010-12-02 03:15:59 +08:00
|
|
|
return NULL;
|
2006-04-07 14:58:51 +08:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:51:49 +08:00
|
|
|
static int read_graft_file(struct repository *r, const char *graft_file)
|
2005-07-30 15:58:28 +08:00
|
|
|
{
|
2017-05-03 18:16:50 +08:00
|
|
|
FILE *fp = fopen_or_warn(graft_file, "r");
|
2013-12-28 04:49:57 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2006-04-07 14:58:51 +08:00
|
|
|
if (!fp)
|
|
|
|
return -1;
|
Deprecate support for .git/info/grafts
The grafts feature was a convenient way to "stitch together" ancient
history to the fresh start of linux.git.
Its implementation is, however, not up to Git's standards, as there are
too many ways where it can lead to surprising and unwelcome behavior.
For example, when pushing from a repository with active grafts, it is
possible to miss commits that have been "grafted out", resulting in a
broken state on the other side.
Also, the grafts feature is limited to "rewriting" commits' list of
parents, it cannot replace anything else.
The much younger feature implemented as `git replace` set out to remedy
those limitations and dangerous bugs.
Seeing as `git replace` is pretty mature by now (since 4228e8bc98
(replace: add --graft option, 2014-07-19) it can perform the graft
file's duties), it is time to deprecate support for the graft file, and
to retire it eventually.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Reviewed-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-29 06:44:44 +08:00
|
|
|
if (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"
|
|
|
|
"Please use \"git replace --convert-graft-file\"\n"
|
|
|
|
"to convert the grafts into replace refs.\n"
|
|
|
|
"\n"
|
|
|
|
"Turn this message off by running\n"
|
|
|
|
"\"git config advice.graftFileDeprecated false\""));
|
2013-12-28 04:49:57 +08:00
|
|
|
while (!strbuf_getwholeline(&buf, fp, '\n')) {
|
2005-07-30 15:58:28 +08:00
|
|
|
/* The format is just "Commit Parent1 Parent2 ...\n" */
|
2017-08-19 02:33:12 +08:00
|
|
|
struct commit_graft *graft = read_graft_line(&buf);
|
2006-04-17 05:24:56 +08:00
|
|
|
if (!graft)
|
|
|
|
continue;
|
2018-05-18 06:51:49 +08:00
|
|
|
if (register_commit_graft(r, graft, 1))
|
2013-12-28 04:49:57 +08:00
|
|
|
error("duplicate graft data: %s", buf.buf);
|
2005-07-30 15:58:28 +08:00
|
|
|
}
|
|
|
|
fclose(fp);
|
2013-12-28 04:49:57 +08:00
|
|
|
strbuf_release(&buf);
|
2006-04-07 14:58:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:51:53 +08:00
|
|
|
static void prepare_commit_graft(struct repository *r)
|
2006-04-07 14:58:51 +08:00
|
|
|
{
|
|
|
|
char *graft_file;
|
|
|
|
|
2018-05-18 06:51:53 +08:00
|
|
|
if (r->parsed_objects->commit_graft_prepared)
|
2006-04-07 14:58:51 +08:00
|
|
|
return;
|
prepare_commit_graft: treat non-repository as a noop
The parse_commit_buffer() function consults lookup_commit_graft()
to see if we need to rewrite parents. The latter will look
at $GIT_DIR/info/grafts. If you're outside of a repository,
then this will trigger a BUG() as of b1ef400eec (setup_git_env:
avoid blind fall-back to ".git", 2016-10-20).
It's probably uncommon to actually parse a commit outside of
a repository, but you can see it in action with:
cd /not/a/git/repo
git index-pack --strict /some/file.pack
This works fine without --strict, but the fsck checks will
try to parse any commits, triggering the BUG(). We can fix
that by teaching the graft code to behave as if there are no
grafts when we aren't in a repository.
Arguably index-pack (and fsck) are wrong to consider grafts
at all. So another solution is to disable grafts entirely
for those commands. But given that the graft feature is
deprecated anyway, it's not worth even thinking through the
ramifications that might have.
There is one other corner case I considered here. What
should:
cd /not/a/git/repo
export GIT_GRAFT_FILE=/file/with/grafts
git index-pack --strict /some/file.pack
do? We don't have a repository, but the user has pointed us
directly at a graft file, which we could respect. I believe
this case did work that way prior to b1ef400eec. However,
fixing it now would be pretty invasive. Back then we would
just call into setup_git_env() even without a repository.
But these days it actually takes a git_dir argument. So
there would be a fair bit of refactoring of the setup code
involved.
Given the obscurity of this case, plus the fact that grafts
are deprecated and probably shouldn't work under index-pack
anyway, it's not worth pursuing further. This patch at least
un-breaks the common case where you're _not_ using grafts,
but we BUG() anyway trying to even find that out.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-06-01 06:42:53 +08:00
|
|
|
if (!startup_info->have_repository)
|
|
|
|
return;
|
|
|
|
|
2018-05-18 06:51:53 +08:00
|
|
|
graft_file = get_graft_file(r);
|
|
|
|
read_graft_file(r, graft_file);
|
2006-10-31 03:09:06 +08:00
|
|
|
/* make sure shallows are read */
|
2018-05-18 06:51:53 +08:00
|
|
|
is_repository_shallow(r);
|
|
|
|
r->parsed_objects->commit_graft_prepared = 1;
|
2005-07-30 15:58:28 +08:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:51:54 +08:00
|
|
|
struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid)
|
2005-07-30 15:58:28 +08:00
|
|
|
{
|
|
|
|
int pos;
|
2018-05-18 06:51:54 +08:00
|
|
|
prepare_commit_graft(r);
|
|
|
|
pos = commit_graft_pos(r, oid->hash);
|
2005-07-30 15:58:28 +08:00
|
|
|
if (pos < 0)
|
|
|
|
return NULL;
|
2018-05-18 06:51:54 +08:00
|
|
|
return r->parsed_objects->grafts[pos];
|
2005-07-30 15:58:28 +08:00
|
|
|
}
|
|
|
|
|
2011-08-18 20:29:35 +08:00
|
|
|
int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
|
2006-10-31 03:09:06 +08:00
|
|
|
{
|
2011-08-18 20:29:35 +08:00
|
|
|
int i, ret;
|
2018-05-16 07:42:16 +08:00
|
|
|
for (i = ret = 0; i < the_repository->parsed_objects->grafts_nr && !ret; i++)
|
|
|
|
ret = fn(the_repository->parsed_objects->grafts[i], cb_data);
|
2011-08-18 20:29:35 +08:00
|
|
|
return ret;
|
2006-10-31 03:09:06 +08:00
|
|
|
}
|
|
|
|
|
2017-05-07 06:10:06 +08:00
|
|
|
int unregister_shallow(const struct object_id *oid)
|
2006-10-31 03:09:53 +08:00
|
|
|
{
|
2018-05-16 07:42:17 +08:00
|
|
|
int pos = commit_graft_pos(the_repository, oid->hash);
|
2006-10-31 03:09:53 +08:00
|
|
|
if (pos < 0)
|
|
|
|
return -1;
|
2018-05-16 07:42:16 +08:00
|
|
|
if (pos + 1 < the_repository->parsed_objects->grafts_nr)
|
|
|
|
MOVE_ARRAY(the_repository->parsed_objects->grafts + pos,
|
|
|
|
the_repository->parsed_objects->grafts + pos + 1,
|
|
|
|
the_repository->parsed_objects->grafts_nr - pos - 1);
|
|
|
|
the_repository->parsed_objects->grafts_nr--;
|
2006-10-31 03:09:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-11 05:44:13 +08:00
|
|
|
struct commit_buffer {
|
|
|
|
void *buffer;
|
|
|
|
unsigned long size;
|
|
|
|
};
|
|
|
|
define_commit_slab(buffer_slab, struct commit_buffer);
|
2014-06-11 05:43:02 +08:00
|
|
|
|
2018-06-29 09:22:15 +08:00
|
|
|
struct buffer_slab *allocate_commit_buffer_slab(void)
|
2014-06-11 05:40:14 +08:00
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct buffer_slab *bs = xmalloc(sizeof(*bs));
|
|
|
|
init_buffer_slab(bs);
|
|
|
|
return bs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_commit_buffer_slab(struct buffer_slab *bs)
|
|
|
|
{
|
|
|
|
clear_buffer_slab(bs);
|
|
|
|
free(bs);
|
|
|
|
}
|
2014-06-11 05:43:02 +08:00
|
|
|
|
2018-06-29 09:22:16 +08:00
|
|
|
void set_commit_buffer(struct repository *r, struct commit *commit, void *buffer, unsigned long size)
|
2014-06-11 05:40:14 +08:00
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct commit_buffer *v = buffer_slab_at(
|
2018-06-29 09:22:16 +08:00
|
|
|
r->parsed_objects->buffer_slab, commit);
|
2014-06-11 05:44:13 +08:00
|
|
|
v->buffer = buffer;
|
|
|
|
v->size = size;
|
2014-06-11 05:40:14 +08:00
|
|
|
}
|
|
|
|
|
2018-06-29 09:22:17 +08:00
|
|
|
const void *get_cached_commit_buffer(struct repository *r, const struct commit *commit, unsigned long *sizep)
|
2014-06-11 05:40:39 +08:00
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct commit_buffer *v = buffer_slab_peek(
|
2018-06-29 09:22:17 +08:00
|
|
|
r->parsed_objects->buffer_slab, commit);
|
2015-05-15 06:25:52 +08:00
|
|
|
if (!v) {
|
|
|
|
if (sizep)
|
|
|
|
*sizep = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-11 05:44:13 +08:00
|
|
|
if (sizep)
|
|
|
|
*sizep = v->size;
|
|
|
|
return v->buffer;
|
2014-06-11 05:40:39 +08:00
|
|
|
}
|
|
|
|
|
2014-06-11 05:44:13 +08:00
|
|
|
const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
|
2014-06-11 05:40:39 +08:00
|
|
|
{
|
2018-06-29 09:22:02 +08:00
|
|
|
const void *ret = get_cached_commit_buffer(the_repository, commit, sizep);
|
2014-06-11 05:40:39 +08:00
|
|
|
if (!ret) {
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:53 +08:00
|
|
|
ret = read_object_file(&commit->object.oid, &type, &size);
|
2014-06-11 05:40:39 +08:00
|
|
|
if (!ret)
|
|
|
|
die("cannot read commit object %s",
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&commit->object.oid));
|
2014-06-11 05:40:39 +08:00
|
|
|
if (type != OBJ_COMMIT)
|
|
|
|
die("expected commit for %s, got %s",
|
2018-02-15 02:59:24 +08:00
|
|
|
oid_to_hex(&commit->object.oid), type_name(type));
|
2014-06-11 05:44:13 +08:00
|
|
|
if (sizep)
|
|
|
|
*sizep = size;
|
2014-06-11 05:40:39 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void unuse_commit_buffer(const struct commit *commit, const void *buffer)
|
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct commit_buffer *v = buffer_slab_peek(
|
|
|
|
the_repository->parsed_objects->buffer_slab, commit);
|
2015-05-15 06:25:52 +08:00
|
|
|
if (!(v && v->buffer == buffer))
|
2014-06-11 05:40:39 +08:00
|
|
|
free((void *)buffer);
|
|
|
|
}
|
|
|
|
|
provide a helper to free commit buffer
This converts two lines into one at each caller. But more
importantly, it abstracts the concept of freeing the buffer,
which will make it easier to change later.
Note that we also need to provide a "detach" mechanism for a
tricky case in index-pack. We are passed a buffer for the
object generated by processing the incoming pack. If we are
not using --strict, we just calculate the sha1 on that
buffer and return, leaving the caller to free it. But if we
are using --strict, we actually attach that buffer to an
object, pass the object to the fsck functions, and then
detach the buffer from the object again (so that the caller
can free it as usual). In this case, we don't want to free
the buffer ourselves, but just make sure it is no longer
associated with the commit.
Note that we are making the assumption here that the
attach/detach process does not impact the buffer at all
(e.g., it is never reallocated or modified). That holds true
now, and we have no plans to change that. However, as we
abstract the commit_buffer code, this dependency becomes
less obvious. So when we detach, let's also make sure that
we get back the same buffer that we gave to the
commit_buffer code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 06:05:37 +08:00
|
|
|
void free_commit_buffer(struct commit *commit)
|
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct commit_buffer *v = buffer_slab_peek(
|
|
|
|
the_repository->parsed_objects->buffer_slab, commit);
|
2015-05-15 06:25:52 +08:00
|
|
|
if (v) {
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(v->buffer);
|
2015-05-15 06:25:52 +08:00
|
|
|
v->size = 0;
|
|
|
|
}
|
provide a helper to free commit buffer
This converts two lines into one at each caller. But more
importantly, it abstracts the concept of freeing the buffer,
which will make it easier to change later.
Note that we also need to provide a "detach" mechanism for a
tricky case in index-pack. We are passed a buffer for the
object generated by processing the incoming pack. If we are
not using --strict, we just calculate the sha1 on that
buffer and return, leaving the caller to free it. But if we
are using --strict, we actually attach that buffer to an
object, pass the object to the fsck functions, and then
detach the buffer from the object again (so that the caller
can free it as usual). In this case, we don't want to free
the buffer ourselves, but just make sure it is no longer
associated with the commit.
Note that we are making the assumption here that the
attach/detach process does not impact the buffer at all
(e.g., it is never reallocated or modified). That holds true
now, and we have no plans to change that. However, as we
abstract the commit_buffer code, this dependency becomes
less obvious. So when we detach, let's also make sure that
we get back the same buffer that we gave to the
commit_buffer code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 06:05:37 +08:00
|
|
|
}
|
|
|
|
|
2018-04-07 03:09:34 +08:00
|
|
|
struct tree *get_commit_tree(const struct commit *commit)
|
|
|
|
{
|
2018-04-07 03:09:46 +08:00
|
|
|
if (commit->maybe_tree || !commit->object.parsed)
|
|
|
|
return commit->maybe_tree;
|
|
|
|
|
|
|
|
if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
|
|
|
|
BUG("commit has NULL tree, but was not loaded from commit-graph");
|
|
|
|
|
2018-07-12 06:42:42 +08:00
|
|
|
return get_commit_tree_in_graph(the_repository, commit);
|
2018-04-07 03:09:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct object_id *get_commit_tree_oid(const struct commit *commit)
|
|
|
|
{
|
|
|
|
return &get_commit_tree(commit)->object.oid;
|
|
|
|
}
|
|
|
|
|
2018-05-16 05:48:42 +08:00
|
|
|
void release_commit_memory(struct commit *c)
|
|
|
|
{
|
2018-06-26 04:22:38 +08:00
|
|
|
c->maybe_tree = NULL;
|
2018-05-16 05:48:42 +08:00
|
|
|
c->index = 0;
|
|
|
|
free_commit_buffer(c);
|
|
|
|
free_commit_list(c->parents);
|
|
|
|
/* TODO: what about commit->util? */
|
|
|
|
|
|
|
|
c->object.parsed = 0;
|
|
|
|
}
|
|
|
|
|
2014-06-11 05:44:13 +08:00
|
|
|
const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
|
provide a helper to free commit buffer
This converts two lines into one at each caller. But more
importantly, it abstracts the concept of freeing the buffer,
which will make it easier to change later.
Note that we also need to provide a "detach" mechanism for a
tricky case in index-pack. We are passed a buffer for the
object generated by processing the incoming pack. If we are
not using --strict, we just calculate the sha1 on that
buffer and return, leaving the caller to free it. But if we
are using --strict, we actually attach that buffer to an
object, pass the object to the fsck functions, and then
detach the buffer from the object again (so that the caller
can free it as usual). In this case, we don't want to free
the buffer ourselves, but just make sure it is no longer
associated with the commit.
Note that we are making the assumption here that the
attach/detach process does not impact the buffer at all
(e.g., it is never reallocated or modified). That holds true
now, and we have no plans to change that. However, as we
abstract the commit_buffer code, this dependency becomes
less obvious. So when we detach, let's also make sure that
we get back the same buffer that we gave to the
commit_buffer code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 06:05:37 +08:00
|
|
|
{
|
2018-06-29 09:22:15 +08:00
|
|
|
struct commit_buffer *v = buffer_slab_peek(
|
|
|
|
the_repository->parsed_objects->buffer_slab, commit);
|
2014-06-11 05:44:13 +08:00
|
|
|
void *ret;
|
|
|
|
|
2015-05-15 06:25:52 +08:00
|
|
|
if (!v) {
|
|
|
|
if (sizep)
|
|
|
|
*sizep = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-11 05:44:13 +08:00
|
|
|
ret = v->buffer;
|
|
|
|
if (sizep)
|
|
|
|
*sizep = v->size;
|
|
|
|
|
|
|
|
v->buffer = NULL;
|
|
|
|
v->size = 0;
|
provide a helper to free commit buffer
This converts two lines into one at each caller. But more
importantly, it abstracts the concept of freeing the buffer,
which will make it easier to change later.
Note that we also need to provide a "detach" mechanism for a
tricky case in index-pack. We are passed a buffer for the
object generated by processing the incoming pack. If we are
not using --strict, we just calculate the sha1 on that
buffer and return, leaving the caller to free it. But if we
are using --strict, we actually attach that buffer to an
object, pass the object to the fsck functions, and then
detach the buffer from the object again (so that the caller
can free it as usual). In this case, we don't want to free
the buffer ourselves, but just make sure it is no longer
associated with the commit.
Note that we are making the assumption here that the
attach/detach process does not impact the buffer at all
(e.g., it is never reallocated or modified). That holds true
now, and we have no plans to change that. However, as we
abstract the commit_buffer code, this dependency becomes
less obvious. So when we detach, let's also make sure that
we get back the same buffer that we gave to the
commit_buffer code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 06:05:37 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-06-29 09:22:13 +08:00
|
|
|
int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph)
|
2005-04-19 02:39:48 +08:00
|
|
|
{
|
2011-02-05 18:52:20 +08:00
|
|
|
const char *tail = buffer;
|
|
|
|
const char *bufptr = buffer;
|
2015-03-14 07:39:34 +08:00
|
|
|
struct object_id parent;
|
2005-06-21 11:26:03 +08:00
|
|
|
struct commit_list **pptr;
|
2005-07-30 15:58:28 +08:00
|
|
|
struct commit_graft *graft;
|
2018-07-16 09:27:56 +08:00
|
|
|
const int tree_entry_len = the_hash_algo->hexsz + 5;
|
|
|
|
const int parent_entry_len = the_hash_algo->hexsz + 7;
|
2005-05-07 01:48:34 +08:00
|
|
|
|
2005-04-19 02:39:48 +08:00
|
|
|
if (item->object.parsed)
|
|
|
|
return 0;
|
|
|
|
item->object.parsed = 1;
|
2006-06-28 18:51:00 +08:00
|
|
|
tail += size;
|
2015-03-14 07:39:34 +08:00
|
|
|
if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
|
|
|
|
bufptr[tree_entry_len] != '\n')
|
2015-11-10 10:22:28 +08:00
|
|
|
return error("bogus commit object %s", oid_to_hex(&item->object.oid));
|
2018-05-02 08:25:47 +08:00
|
|
|
if (get_oid_hex(bufptr + 5, &parent) < 0)
|
2006-02-23 09:47:10 +08:00
|
|
|
return error("bad tree pointer in commit %s",
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&item->object.oid));
|
2018-06-29 09:22:13 +08:00
|
|
|
item->maybe_tree = lookup_tree(r, &parent);
|
2015-03-14 07:39:34 +08:00
|
|
|
bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
|
2005-06-21 11:26:03 +08:00
|
|
|
pptr = &item->parents;
|
2005-07-30 15:58:28 +08:00
|
|
|
|
2018-06-29 09:22:13 +08:00
|
|
|
graft = lookup_commit_graft(r, &item->object.oid);
|
2015-03-14 07:39:34 +08:00
|
|
|
while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
|
2005-07-28 06:12:48 +08:00
|
|
|
struct commit *new_parent;
|
|
|
|
|
2015-03-14 07:39:34 +08:00
|
|
|
if (tail <= bufptr + parent_entry_len + 1 ||
|
2018-05-02 08:25:47 +08:00
|
|
|
get_oid_hex(bufptr + 7, &parent) ||
|
2015-03-14 07:39:34 +08:00
|
|
|
bufptr[parent_entry_len] != '\n')
|
2015-11-10 10:22:28 +08:00
|
|
|
return error("bad parents in commit %s", oid_to_hex(&item->object.oid));
|
2015-03-14 07:39:34 +08:00
|
|
|
bufptr += parent_entry_len + 1;
|
2009-07-23 23:33:49 +08:00
|
|
|
/*
|
|
|
|
* The clone is shallow if nr_parent < 0, and we must
|
|
|
|
* not traverse its real parents even when we unhide them.
|
|
|
|
*/
|
|
|
|
if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
|
2005-07-30 15:58:28 +08:00
|
|
|
continue;
|
2018-06-29 09:22:13 +08:00
|
|
|
new_parent = lookup_commit(r, &parent);
|
2008-06-08 04:38:37 +08:00
|
|
|
if (new_parent)
|
2005-06-21 11:26:03 +08:00
|
|
|
pptr = &commit_list_insert(new_parent, pptr)->next;
|
2005-07-30 15:58:28 +08:00
|
|
|
}
|
|
|
|
if (graft) {
|
|
|
|
int i;
|
|
|
|
struct commit *new_parent;
|
|
|
|
for (i = 0; i < graft->nr_parent; i++) {
|
2018-06-29 09:22:13 +08:00
|
|
|
new_parent = lookup_commit(r,
|
2018-06-29 09:21:59 +08:00
|
|
|
&graft->parent[i]);
|
2005-07-30 15:58:28 +08:00
|
|
|
if (!new_parent)
|
|
|
|
continue;
|
|
|
|
pptr = &commit_list_insert(new_parent, pptr)->next;
|
|
|
|
}
|
2005-04-19 02:39:48 +08:00
|
|
|
}
|
2008-01-20 01:35:23 +08:00
|
|
|
item->date = parse_commit_date(bufptr, tail);
|
2005-11-17 13:32:44 +08:00
|
|
|
|
2018-05-01 20:47:13 +08:00
|
|
|
if (check_graph)
|
2018-07-12 06:42:42 +08:00
|
|
|
load_commit_graph_info(the_repository, item);
|
2018-05-01 20:47:13 +08:00
|
|
|
|
2005-04-19 02:39:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-27 21:24:30 +08:00
|
|
|
int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph)
|
2005-05-07 01:48:34 +08:00
|
|
|
{
|
2007-02-27 03:55:59 +08:00
|
|
|
enum object_type type;
|
2005-05-07 01:48:34 +08:00
|
|
|
void *buffer;
|
|
|
|
unsigned long size;
|
|
|
|
int ret;
|
|
|
|
|
2008-02-19 04:48:02 +08:00
|
|
|
if (!item)
|
|
|
|
return -1;
|
2005-05-07 01:48:34 +08:00
|
|
|
if (item->object.parsed)
|
|
|
|
return 0;
|
2018-07-12 06:42:42 +08:00
|
|
|
if (use_commit_graph && parse_commit_in_graph(the_repository, item))
|
2018-04-10 20:56:05 +08:00
|
|
|
return 0;
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:53 +08:00
|
|
|
buffer = read_object_file(&item->object.oid, &type, &size);
|
2005-05-07 01:48:34 +08:00
|
|
|
if (!buffer)
|
add quieter versions of parse_{tree,commit}
When we call parse_commit, it will complain to stderr if the
object does not exist or cannot be read. This means that we
may produce useless error messages if this situation is
expected (e.g., because the object is marked UNINTERESTING,
or because revs->ignore_missing_links is set).
We can fix this by adding a new "parse_X_gently" form that
takes a flag to suppress the messages. The existing
"parse_X" form is already gentle in the sense that it
returns an error rather than dying, and we could in theory
just add a "quiet" flag to it (with existing callers passing
"0"). But doing it this way means we do not have to disturb
existing callers.
Note also that the new flag is "quiet_on_missing", and not
just "quiet". We could add a flag to suppress _all_ errors,
but besides being a more invasive change (we would have to
pass the flag down to sub-functions, too), there is a good
reason not to: we would never want to use it. Missing a
linked object is expected in some circumstances, but it is
never expected to have a malformed commit, or to get a tree
when we wanted a commit. We should always complain about
these corruptions.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-01 17:56:26 +08:00
|
|
|
return quiet_on_missing ? -1 :
|
|
|
|
error("Could not read %s",
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&item->object.oid));
|
2007-02-27 03:55:59 +08:00
|
|
|
if (type != OBJ_COMMIT) {
|
2005-05-07 01:48:34 +08:00
|
|
|
free(buffer);
|
|
|
|
return error("Object %s not a commit",
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&item->object.oid));
|
2005-05-07 01:48:34 +08:00
|
|
|
}
|
2018-06-27 21:24:30 +08:00
|
|
|
|
2018-06-29 09:22:00 +08:00
|
|
|
ret = parse_commit_buffer(the_repository, item, buffer, size, 0);
|
[PATCH] Avoid wasting memory in git-rev-list
As pointed out on the list, git-rev-list can use a lot of memory.
One low-hanging fruit is to free the commit buffer for commits that we
parse. By default, parse_commit() will save away the buffer, since a lot
of cases do want it, and re-reading it continually would be unnecessary.
However, in many cases the buffer isn't actually necessary and saving it
just wastes memory.
We could just free the buffer ourselves, but especially in git-rev-list,
we actually end up using the helper functions that automatically add
parent commits to the commit lists, so we don't actually control the
commit parsing directly.
Instead, just make this behaviour of "parse_commit()" a global flag.
Maybe this is a bit tasteless, but it's very simple, and it makes a
noticable difference in memory usage.
Before the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.02system 0:00.28elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3714minor)pagefaults 0swaps
after the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.00system 0:00.27elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2433minor)pagefaults 0swaps
note how the minor faults have decreased from 3714 pages to 2433 pages.
That's all due to the fewer anonymous pages allocated to hold the comment
buffers and their metadata.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-09-16 05:43:17 +08:00
|
|
|
if (save_commit_buffer && !ret) {
|
2018-06-29 09:22:01 +08:00
|
|
|
set_commit_buffer(the_repository, item, buffer, size);
|
2005-05-26 09:27:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2005-05-07 01:48:34 +08:00
|
|
|
free(buffer);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-06-27 21:24:30 +08:00
|
|
|
int parse_commit_gently(struct commit *item, int quiet_on_missing)
|
|
|
|
{
|
|
|
|
return parse_commit_internal(item, quiet_on_missing, 1);
|
|
|
|
}
|
|
|
|
|
2013-10-24 16:52:36 +08:00
|
|
|
void parse_commit_or_die(struct commit *item)
|
|
|
|
{
|
|
|
|
if (parse_commit(item))
|
|
|
|
die("unable to parse commit %s",
|
2015-11-10 10:22:28 +08:00
|
|
|
item ? oid_to_hex(&item->object.oid) : "(null)");
|
2013-10-24 16:52:36 +08:00
|
|
|
}
|
|
|
|
|
2010-07-22 21:18:30 +08:00
|
|
|
int find_commit_subject(const char *commit_buffer, const char **subject)
|
|
|
|
{
|
|
|
|
const char *eol;
|
|
|
|
const char *p = commit_buffer;
|
|
|
|
|
|
|
|
while (*p && (*p != '\n' || p[1] != '\n'))
|
|
|
|
p++;
|
|
|
|
if (*p) {
|
2016-06-23 04:20:20 +08:00
|
|
|
p = skip_blank_lines(p + 2);
|
2017-01-28 10:01:41 +08:00
|
|
|
eol = strchrnul(p, '\n');
|
2010-07-22 21:18:30 +08:00
|
|
|
} else
|
|
|
|
eol = p;
|
|
|
|
|
|
|
|
*subject = p;
|
|
|
|
|
|
|
|
return eol - p;
|
|
|
|
}
|
|
|
|
|
2005-05-31 09:44:02 +08:00
|
|
|
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
|
2005-04-24 09:47:23 +08:00
|
|
|
{
|
2005-04-27 03:00:58 +08:00
|
|
|
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
|
2005-04-24 09:47:23 +08:00
|
|
|
new_list->item = item;
|
|
|
|
new_list->next = *list_p;
|
|
|
|
*list_p = new_list;
|
2005-05-31 09:44:02 +08:00
|
|
|
return new_list;
|
2005-04-24 09:47:23 +08:00
|
|
|
}
|
|
|
|
|
2008-06-28 00:21:55 +08:00
|
|
|
unsigned commit_list_count(const struct commit_list *l)
|
|
|
|
{
|
|
|
|
unsigned c = 0;
|
|
|
|
for (; l; l = l->next )
|
|
|
|
c++;
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
log: use true parents for diff even when rewriting
When using pathspec filtering in combination with diff-based log
output, parent simplification happens before the diff is computed.
The diff is therefore against the *simplified* parents.
This works okay, arguably by accident, in the normal case:
simplification reduces to one parent as long as the commit is TREESAME
to it. So the simplified parent of any given commit must have the
same tree contents on the filtered paths as its true (unfiltered)
parent.
However, --full-diff breaks this guarantee, and indeed gives pretty
spectacular results when comparing the output of
git log --graph --stat ...
git log --graph --full-diff --stat ...
(--graph internally kicks in parent simplification, much like
--parents).
To fix it, store a copy of the parent list before simplification (in a
slab) whenever --full-diff is in effect. Then use the stored parents
instead of the simplified ones in the commit display code paths. The
latter do not actually check for --full-diff to avoid duplicated code;
they just grab the original parents if save_parents() has not been
called for this revision walk.
For ordinary commits it should be obvious that this is the right thing
to do.
Merge commits are a bit subtle. Observe that with default
simplification, merge simplification is an all-or-nothing decision:
either the merge is TREESAME to one parent and disappears, or it is
different from all parents and the parent list remains intact.
Redundant parents are not pruned, so the existing code also shows them
as a merge.
So if we do show a merge commit, the parent list just consists of the
rewrite result on each parent. Running, e.g., --cc on this in
--full-diff mode is not very useful: if any commits were skipped, some
hunks will disagree with all sides of the merge (with one side,
because commits were skipped; with the others, because they didn't
have those changes in the first place). This triggers --cc showing
these hunks spuriously.
Therefore I believe that even for merge commits it is better to show
the diffs wrt. the original parents.
Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Thomas Rast <trast@inf.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-01 04:13:20 +08:00
|
|
|
struct commit_list *copy_commit_list(struct commit_list *list)
|
|
|
|
{
|
|
|
|
struct commit_list *head = NULL;
|
|
|
|
struct commit_list **pp = &head;
|
|
|
|
while (list) {
|
2014-07-10 17:47:47 +08:00
|
|
|
pp = commit_list_append(list->item, pp);
|
log: use true parents for diff even when rewriting
When using pathspec filtering in combination with diff-based log
output, parent simplification happens before the diff is computed.
The diff is therefore against the *simplified* parents.
This works okay, arguably by accident, in the normal case:
simplification reduces to one parent as long as the commit is TREESAME
to it. So the simplified parent of any given commit must have the
same tree contents on the filtered paths as its true (unfiltered)
parent.
However, --full-diff breaks this guarantee, and indeed gives pretty
spectacular results when comparing the output of
git log --graph --stat ...
git log --graph --full-diff --stat ...
(--graph internally kicks in parent simplification, much like
--parents).
To fix it, store a copy of the parent list before simplification (in a
slab) whenever --full-diff is in effect. Then use the stored parents
instead of the simplified ones in the commit display code paths. The
latter do not actually check for --full-diff to avoid duplicated code;
they just grab the original parents if save_parents() has not been
called for this revision walk.
For ordinary commits it should be obvious that this is the right thing
to do.
Merge commits are a bit subtle. Observe that with default
simplification, merge simplification is an all-or-nothing decision:
either the merge is TREESAME to one parent and disappears, or it is
different from all parents and the parent list remains intact.
Redundant parents are not pruned, so the existing code also shows them
as a merge.
So if we do show a merge commit, the parent list just consists of the
rewrite result on each parent. Running, e.g., --cc on this in
--full-diff mode is not very useful: if any commits were skipped, some
hunks will disagree with all sides of the merge (with one side,
because commits were skipped; with the others, because they didn't
have those changes in the first place). This triggers --cc showing
these hunks spuriously.
Therefore I believe that even for merge commits it is better to show
the diffs wrt. the original parents.
Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Thomas Rast <trast@inf.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-01 04:13:20 +08:00
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
2005-04-19 02:39:48 +08:00
|
|
|
void free_commit_list(struct commit_list *list)
|
|
|
|
{
|
2015-10-25 00:21:31 +08:00
|
|
|
while (list)
|
|
|
|
pop_commit(&list);
|
2005-04-19 02:39:48 +08:00
|
|
|
}
|
2005-04-24 09:47:23 +08:00
|
|
|
|
2010-11-27 09:58:14 +08:00
|
|
|
struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list)
|
2005-04-24 09:47:23 +08:00
|
|
|
{
|
|
|
|
struct commit_list **pp = list;
|
|
|
|
struct commit_list *p;
|
|
|
|
while ((p = *pp) != NULL) {
|
|
|
|
if (p->item->date < item->date) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pp = &p->next;
|
|
|
|
}
|
2005-07-07 00:31:17 +08:00
|
|
|
return commit_list_insert(item, pp);
|
2005-04-24 09:47:23 +08:00
|
|
|
}
|
|
|
|
|
2012-04-01 06:10:39 +08:00
|
|
|
static int commit_list_compare_by_date(const void *a, const void *b)
|
|
|
|
{
|
2017-04-27 03:29:31 +08:00
|
|
|
timestamp_t a_date = ((const struct commit_list *)a)->item->date;
|
|
|
|
timestamp_t b_date = ((const struct commit_list *)b)->item->date;
|
2012-04-01 06:10:39 +08:00
|
|
|
if (a_date < b_date)
|
|
|
|
return 1;
|
|
|
|
if (a_date > b_date)
|
|
|
|
return -1;
|
|
|
|
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;
|
|
|
|
}
|
2007-06-07 15:04:01 +08:00
|
|
|
|
2010-11-27 09:58:14 +08:00
|
|
|
void commit_list_sort_by_date(struct commit_list **list)
|
2005-04-24 09:47:23 +08:00
|
|
|
{
|
2012-04-18 02:07:01 +08:00
|
|
|
*list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
|
|
|
|
commit_list_compare_by_date);
|
2005-04-24 09:47:23 +08:00
|
|
|
}
|
|
|
|
|
2005-04-24 11:29:22 +08:00
|
|
|
struct commit *pop_most_recent_commit(struct commit_list **list,
|
|
|
|
unsigned int mark)
|
2005-04-24 09:47:23 +08:00
|
|
|
{
|
2015-10-25 00:21:31 +08:00
|
|
|
struct commit *ret = pop_commit(list);
|
2005-04-24 09:47:23 +08:00
|
|
|
struct commit_list *parents = ret->parents;
|
|
|
|
|
|
|
|
while (parents) {
|
2005-04-24 10:21:28 +08:00
|
|
|
struct commit *commit = parents->item;
|
2008-02-19 04:48:03 +08:00
|
|
|
if (!parse_commit(commit) && !(commit->object.flags & mark)) {
|
2005-04-24 11:29:22 +08:00
|
|
|
commit->object.flags |= mark;
|
2010-11-27 09:58:14 +08:00
|
|
|
commit_list_insert_by_date(commit, list);
|
2005-04-24 10:21:28 +08:00
|
|
|
}
|
2005-04-24 09:47:23 +08:00
|
|
|
parents = parents->next;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2005-06-01 23:34:23 +08:00
|
|
|
|
2012-01-14 20:19:53 +08:00
|
|
|
static void clear_commit_marks_1(struct commit_list **plist,
|
|
|
|
struct commit *commit, unsigned int mark)
|
2006-01-08 10:52:42 +08:00
|
|
|
{
|
2007-10-11 06:14:35 +08:00
|
|
|
while (commit) {
|
|
|
|
struct commit_list *parents;
|
2006-01-08 10:52:42 +08:00
|
|
|
|
2007-10-11 06:14:35 +08:00
|
|
|
if (!(mark & commit->object.flags))
|
|
|
|
return;
|
2006-07-05 08:45:22 +08:00
|
|
|
|
2007-10-11 06:14:35 +08:00
|
|
|
commit->object.flags &= ~mark;
|
|
|
|
|
|
|
|
parents = commit->parents;
|
|
|
|
if (!parents)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while ((parents = parents->next))
|
2012-01-14 20:19:53 +08:00
|
|
|
commit_list_insert(parents->item, plist);
|
2007-10-11 06:14:35 +08:00
|
|
|
|
|
|
|
commit = commit->parents->item;
|
2006-01-08 10:52:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-06 03:42:20 +08:00
|
|
|
void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark)
|
2012-01-14 20:19:53 +08:00
|
|
|
{
|
|
|
|
struct commit_list *list = NULL;
|
2013-03-06 03:42:20 +08:00
|
|
|
|
|
|
|
while (nr--) {
|
2017-12-26 01:43:37 +08:00
|
|
|
clear_commit_marks_1(&list, *commit, mark);
|
2013-03-06 03:42:20 +08:00
|
|
|
commit++;
|
|
|
|
}
|
2012-01-14 20:19:53 +08:00
|
|
|
while (list)
|
|
|
|
clear_commit_marks_1(&list, pop_commit(&list), mark);
|
|
|
|
}
|
|
|
|
|
2013-03-06 03:42:20 +08:00
|
|
|
void clear_commit_marks(struct commit *commit, unsigned int mark)
|
|
|
|
{
|
|
|
|
clear_commit_marks_many(1, &commit, mark);
|
|
|
|
}
|
|
|
|
|
2005-06-06 23:39:40 +08:00
|
|
|
struct commit *pop_commit(struct commit_list **stack)
|
|
|
|
{
|
|
|
|
struct commit_list *top = *stack;
|
|
|
|
struct commit *item = top ? top->item : NULL;
|
|
|
|
|
|
|
|
if (top) {
|
|
|
|
*stack = top->next;
|
|
|
|
free(top);
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
/*
|
|
|
|
* Topological sort support
|
|
|
|
*/
|
2013-04-09 14:52:56 +08:00
|
|
|
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
/* count number of children that have not been emitted */
|
|
|
|
define_commit_slab(indegree_slab, int);
|
2013-04-09 14:52:56 +08:00
|
|
|
|
2013-06-08 01:35:54 +08:00
|
|
|
/* record author-date for each commit object */
|
2018-08-22 04:54:12 +08:00
|
|
|
define_commit_slab(author_date_slab, timestamp_t);
|
2013-06-08 01:35:54 +08:00
|
|
|
|
|
|
|
static void record_author_date(struct author_date_slab *author_date,
|
|
|
|
struct commit *commit)
|
|
|
|
{
|
2014-06-11 05:44:13 +08:00
|
|
|
const char *buffer = get_commit_buffer(commit, NULL);
|
2013-06-08 01:35:54 +08:00
|
|
|
struct ident_split ident;
|
2014-08-27 15:56:55 +08:00
|
|
|
const char *ident_line;
|
|
|
|
size_t ident_len;
|
2013-06-08 01:35:54 +08:00
|
|
|
char *date_end;
|
2017-04-27 03:29:31 +08:00
|
|
|
timestamp_t date;
|
2013-06-08 01:35:54 +08:00
|
|
|
|
2014-08-27 15:56:55 +08:00
|
|
|
ident_line = find_commit_header(buffer, "author", &ident_len);
|
|
|
|
if (!ident_line)
|
|
|
|
goto fail_exit; /* no author line */
|
|
|
|
if (split_ident_line(&ident, ident_line, ident_len) ||
|
|
|
|
!ident.date_begin || !ident.date_end)
|
|
|
|
goto fail_exit; /* malformed "author" line */
|
2013-06-08 01:35:54 +08:00
|
|
|
|
2017-04-21 18:45:44 +08:00
|
|
|
date = parse_timestamp(ident.date_begin, &date_end, 10);
|
2013-06-08 01:35:54 +08:00
|
|
|
if (date_end != ident.date_end)
|
|
|
|
goto fail_exit; /* malformed date */
|
|
|
|
*(author_date_slab_at(author_date, commit)) = date;
|
|
|
|
|
|
|
|
fail_exit:
|
2014-06-11 05:41:02 +08:00
|
|
|
unuse_commit_buffer(commit, buffer);
|
2013-06-08 01:35:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_commits_by_author_date(const void *a_, const void *b_,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
const struct commit *a = a_, *b = b_;
|
|
|
|
struct author_date_slab *author_date = cb_data;
|
2017-04-27 03:29:31 +08:00
|
|
|
timestamp_t a_date = *(author_date_slab_at(author_date, a));
|
|
|
|
timestamp_t b_date = *(author_date_slab_at(author_date, b));
|
2013-06-08 01:35:54 +08:00
|
|
|
|
|
|
|
/* newer commits with larger date first */
|
|
|
|
if (a_date < b_date)
|
|
|
|
return 1;
|
|
|
|
else if (a_date > b_date)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-01 20:47:11 +08:00
|
|
|
int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
|
|
|
|
{
|
|
|
|
const struct commit *a = a_, *b = b_;
|
|
|
|
|
|
|
|
/* newer commits first */
|
|
|
|
if (a->generation < b->generation)
|
|
|
|
return 1;
|
|
|
|
else if (a->generation > b->generation)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* use date as a heuristic when generations are equal */
|
|
|
|
if (a->date < b->date)
|
|
|
|
return 1;
|
|
|
|
else if (a->date > b->date)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:21:48 +08:00
|
|
|
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
|
2013-06-07 12:58:12 +08:00
|
|
|
{
|
|
|
|
const struct commit *a = a_, *b = b_;
|
|
|
|
/* newer commits with larger date first */
|
|
|
|
if (a->date < b->date)
|
|
|
|
return 1;
|
|
|
|
else if (a->date > b->date)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-07-07 00:39:34 +08:00
|
|
|
/*
|
|
|
|
* Performs an in-place topological sort on the list supplied.
|
|
|
|
*/
|
2013-06-07 12:58:12 +08:00
|
|
|
void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order)
|
2006-03-10 17:21:37 +08:00
|
|
|
{
|
2007-11-03 04:32:58 +08:00
|
|
|
struct commit_list *next, *orig = *list;
|
|
|
|
struct commit_list **pptr;
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
struct indegree_slab indegree;
|
2013-06-07 12:58:12 +08:00
|
|
|
struct prio_queue queue;
|
|
|
|
struct commit *commit;
|
2013-06-08 01:35:54 +08:00
|
|
|
struct author_date_slab author_date;
|
2007-06-07 15:04:01 +08:00
|
|
|
|
2007-11-03 04:32:58 +08:00
|
|
|
if (!orig)
|
2005-12-24 20:12:43 +08:00
|
|
|
return;
|
2007-11-03 04:32:58 +08:00
|
|
|
*list = NULL;
|
|
|
|
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
init_indegree_slab(&indegree);
|
2013-06-07 12:58:12 +08:00
|
|
|
memset(&queue, '\0', sizeof(queue));
|
2013-06-08 01:35:54 +08:00
|
|
|
|
2013-06-07 12:58:12 +08:00
|
|
|
switch (sort_order) {
|
|
|
|
default: /* REV_SORT_IN_GRAPH_ORDER */
|
|
|
|
queue.compare = NULL;
|
|
|
|
break;
|
|
|
|
case REV_SORT_BY_COMMIT_DATE:
|
|
|
|
queue.compare = compare_commits_by_commit_date;
|
|
|
|
break;
|
2013-06-08 01:35:54 +08:00
|
|
|
case REV_SORT_BY_AUTHOR_DATE:
|
|
|
|
init_author_date_slab(&author_date);
|
|
|
|
queue.compare = compare_commits_by_author_date;
|
|
|
|
queue.cb_data = &author_date;
|
|
|
|
break;
|
2013-06-07 12:58:12 +08:00
|
|
|
}
|
2013-04-09 14:52:56 +08:00
|
|
|
|
2007-11-03 04:32:58 +08:00
|
|
|
/* Mark them and clear the indegree */
|
|
|
|
for (next = orig; next; next = next->next) {
|
|
|
|
struct commit *commit = next->item;
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
*(indegree_slab_at(&indegree, commit)) = 1;
|
2013-06-08 01:35:54 +08:00
|
|
|
/* also record the author dates, if needed */
|
|
|
|
if (sort_order == REV_SORT_BY_AUTHOR_DATE)
|
|
|
|
record_author_date(&author_date, commit);
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
2007-11-03 04:32:58 +08:00
|
|
|
|
2005-07-07 00:39:34 +08:00
|
|
|
/* update the indegree */
|
2007-11-03 04:32:58 +08:00
|
|
|
for (next = orig; next; next = next->next) {
|
2013-06-07 12:58:12 +08:00
|
|
|
struct commit_list *parents = next->item->parents;
|
2005-07-07 00:39:34 +08:00
|
|
|
while (parents) {
|
2007-11-03 04:32:58 +08:00
|
|
|
struct commit *parent = parents->item;
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
int *pi = indegree_slab_at(&indegree, parent);
|
2006-03-10 17:21:37 +08:00
|
|
|
|
2013-04-09 14:52:56 +08:00
|
|
|
if (*pi)
|
|
|
|
(*pi)++;
|
2007-11-03 04:32:58 +08:00
|
|
|
parents = parents->next;
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
|
|
|
}
|
2007-11-03 04:32:58 +08:00
|
|
|
|
2007-06-07 15:04:01 +08:00
|
|
|
/*
|
2007-09-10 18:35:06 +08:00
|
|
|
* find the tips
|
|
|
|
*
|
|
|
|
* tips are nodes not reachable from any other node in the list
|
|
|
|
*
|
|
|
|
* the tips serve as a starting set for the work queue.
|
|
|
|
*/
|
2007-11-03 04:32:58 +08:00
|
|
|
for (next = orig; next; next = next->next) {
|
|
|
|
struct commit *commit = next->item;
|
2005-07-07 00:39:34 +08:00
|
|
|
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
if (*(indegree_slab_at(&indegree, commit)) == 1)
|
2013-06-07 12:58:12 +08:00
|
|
|
prio_queue_put(&queue, commit);
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
2006-02-16 14:05:33 +08:00
|
|
|
|
2013-06-07 12:58:12 +08:00
|
|
|
/*
|
|
|
|
* This is unfortunate; the initial tips need to be shown
|
|
|
|
* in the order given from the revision traversal machinery.
|
|
|
|
*/
|
|
|
|
if (sort_order == REV_SORT_IN_GRAPH_ORDER)
|
|
|
|
prio_queue_reverse(&queue);
|
|
|
|
|
|
|
|
/* We no longer need the commit list */
|
|
|
|
free_commit_list(orig);
|
2007-11-03 04:32:58 +08:00
|
|
|
|
|
|
|
pptr = list;
|
|
|
|
*list = NULL;
|
2013-06-07 12:58:12 +08:00
|
|
|
while ((commit = prio_queue_get(&queue)) != NULL) {
|
|
|
|
struct commit_list *parents;
|
2007-11-03 04:32:58 +08:00
|
|
|
|
|
|
|
for (parents = commit->parents; parents ; parents = parents->next) {
|
2011-08-26 05:46:52 +08:00
|
|
|
struct commit *parent = parents->item;
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
int *pi = indegree_slab_at(&indegree, parent);
|
2007-11-03 04:32:58 +08:00
|
|
|
|
2013-04-09 14:52:56 +08:00
|
|
|
if (!*pi)
|
2007-11-03 04:32:58 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parents are only enqueued for emission
|
|
|
|
* when all their children have been emitted thereby
|
|
|
|
* guaranteeing topological order.
|
|
|
|
*/
|
2013-06-07 12:58:12 +08:00
|
|
|
if (--(*pi) == 1)
|
|
|
|
prio_queue_put(&queue, parent);
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
|
|
|
/*
|
2013-06-07 12:58:12 +08:00
|
|
|
* all children of commit have already been
|
|
|
|
* emitted. we can emit it now.
|
2007-09-10 18:35:06 +08:00
|
|
|
*/
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
*(indegree_slab_at(&indegree, commit)) = 0;
|
2013-06-07 12:58:12 +08:00
|
|
|
|
|
|
|
pptr = &commit_list_insert(commit, pptr)->next;
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
2013-04-09 14:52:56 +08:00
|
|
|
|
commit-slab: introduce a macro to define a slab for new type
Introduce a header file to define a macro that can define the struct
type, initializer, accessor and cleanup functions to manage a commit
slab. Update the "indegree" topological sort facility using it.
To associate 32 flag bits with each commit, you can write:
define_commit_slab(flag32, uint32);
to declare "struct flag32" type, define an instance of it with
struct flag32 flags;
and initialize it by calling
init_flag32(&flags);
After that, a call to flag32_at() function
uint32 *fp = flag32_at(&flags, commit);
will return a pointer pointing at a uint32 for that commit. Once
you are done with these flags, clean them up with
clear_flag32(&flags);
Callers that cannot hard-code how wide the data to be associated
with the commit be at compile time can use the "_with_stride"
variant to initialize the slab.
Suppose you want to give one bit per existing ref, and paint commits
down to find which refs are descendants of each commit. Saying
typedef uint32 bits320[5];
define_commit_slab(flagbits, bits320);
at compile time will still limit your code with hard-coded limit,
because you may find that you have more than 320 refs at runtime.
The code can declare a commit slab "struct flagbits" like this
instead:
define_commit_slab(flagbits, unsigned char);
struct flagbits flags;
and initialize it by:
nrefs = ... count number of refs ...
init_flagbits_with_stride(&flags, (nrefs + 7) / 8);
so that
unsigned char *fp = flagbits_at(&flags, commit);
will return a pointer pointing at an array of 40 "unsigned char"s
associated with the commit, once you figure out nrefs is 320 at
runtime.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-14 02:56:41 +08:00
|
|
|
clear_indegree_slab(&indegree);
|
2013-06-07 12:58:12 +08:00
|
|
|
clear_prio_queue(&queue);
|
2013-06-08 01:35:54 +08:00
|
|
|
if (sort_order == REV_SORT_BY_AUTHOR_DATE)
|
|
|
|
clear_author_date_slab(&author_date);
|
2005-07-07 00:39:34 +08:00
|
|
|
}
|
2006-06-29 21:17:32 +08:00
|
|
|
|
2007-01-09 15:10:49 +08:00
|
|
|
/* merge-base stuff */
|
2006-06-29 21:17:32 +08:00
|
|
|
|
2014-03-25 21:23:26 +08:00
|
|
|
/* Remember to update object flag allocation in object.h */
|
2006-10-23 08:32:47 +08:00
|
|
|
#define PARENT1 (1u<<16)
|
|
|
|
#define PARENT2 (1u<<17)
|
|
|
|
#define STALE (1u<<18)
|
|
|
|
#define RESULT (1u<<19)
|
2006-06-29 21:17:32 +08:00
|
|
|
|
2007-01-09 15:10:49 +08:00
|
|
|
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
|
|
|
|
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
static int queue_has_nonstale(struct prio_queue *queue)
|
2006-06-29 21:17:32 +08:00
|
|
|
{
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < queue->nr; i++) {
|
|
|
|
struct commit *commit = queue->array[i].data;
|
|
|
|
if (!(commit->object.flags & STALE))
|
|
|
|
return 1;
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
return 0;
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
|
|
|
|
2012-10-05 06:37:15 +08:00
|
|
|
/* all input commits in one and twos[] must have been parsed! */
|
2018-05-01 20:47:19 +08:00
|
|
|
static struct commit_list *paint_down_to_common(struct commit *one, int n,
|
|
|
|
struct commit **twos,
|
|
|
|
int min_generation)
|
2006-06-29 21:17:32 +08:00
|
|
|
{
|
2018-05-01 20:47:11 +08:00
|
|
|
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
|
2006-06-29 21:17:32 +08:00
|
|
|
struct commit_list *result = NULL;
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
int i;
|
2018-05-01 20:47:19 +08:00
|
|
|
uint32_t last_gen = GENERATION_NUMBER_INFINITY;
|
2006-06-29 21:17:32 +08:00
|
|
|
|
commit: don't use generation numbers if not needed
In 3afc679b "commit: use generations in paint_down_to_common()",
the queue in paint_down_to_common() was changed to use a priority
order based on generation number before commit date. This served
two purposes:
1. When generation numbers are present, the walk guarantees
correct topological relationships, regardless of clock skew in
commit dates.
2. It enables short-circuiting the walk when the min_generation
parameter is added in d7c1ec3e "commit: add short-circuit to
paint_down_to_common()". This short-circuit helps commands
like 'git branch --contains' from needing to walk to a merge
base when we know the result is false.
The commit message for 3afc679b includes the following sentence:
This change does not affect the number of commits that are
walked during the execution of paint_down_to_common(), only
the order that those commits are inspected.
This statement is incorrect. Because it changes the order in which
the commits are inspected, it changes the order they are added to
the queue, and hence can change the number of loops before the
queue_has_nonstale() method returns true.
This change makes a concrete difference depending on the topology
of the commit graph. For instance, computing the merge-base between
consecutive versions of the Linux kernel has no effect for versions
after v4.9, but 'git merge-base v4.8 v4.9' presents a performance
regression:
v2.18.0: 0.122s
v2.19.0-rc1: 0.547s
HEAD: 0.127s
To determine that this was simply an ordering issue, I inserted
a counter within the while loop of paint_down_to_common() and
found that the loop runs 167,468 times in v2.18.0 and 635,579
times in v2.19.0-rc1.
The topology of this case can be described in a simplified way
here:
v4.9
| \
| \
v4.8 \
| \ \
| \ |
... A B
| / /
| / /
|/__/
C
Here, the "..." means "a very long line of commits". By generation
number, A and B have generation one more than C. However, A and B
have commit date higher than most of the commits reachable from
v4.8. When the walk reaches v4.8, we realize that it has PARENT1
and PARENT2 flags, so everything it can reach is marked as STALE,
including A. B has only the PARENT1 flag, so is not STALE.
When paint_down_to_common() is run using
compare_commits_by_commit_date, A and B are removed from the queue
early and C is inserted into the queue. At this point, C and the
rest of the queue entries are marked as STALE. The loop then
terminates.
When paint_down_to_common() is run using
compare_commits_by_gen_then_commit_date, B is removed from the
queue only after the many commits reachable from v4.8 are explored.
This causes the loop to run longer. The reason for this regression
is simple: the queue order is intended to not explore a commit
until everything that _could_ reach that commit is explored. From
the information gathered by the original ordering, we have no
guarantee that there is not a commit D reachable from v4.8 that
can also reach B. We gained absolute correctness in exchange for
a performance regression.
The performance regression is probably the worse option, since
these incorrect results in paint_down_to_common() are rare. The
topology required for the performance regression are less rare,
but still require multiple merge commits where the parents differ
greatly in generation number. In our example above, the commit A
is as important as the commit B to demonstrate the problem, since
otherwise the commit C will sit in the queue as non-stale just as
long in both orders.
The solution provided uses the min_generation parameter to decide
if we should use generation numbers in our ordering. When
min_generation is equal to zero, it means that the caller has no
known cutoff for the walk, so we should rely on our commit-date
heuristic as before; this is the case with merge_bases_many().
When min_generation is non-zero, then the caller knows a valuable
cutoff for the short-circuit mechanism; this is the case with
remove_redundant() and in_merge_bases_many().
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-30 20:58:09 +08:00
|
|
|
if (!min_generation)
|
|
|
|
queue.compare = compare_commits_by_commit_date;
|
|
|
|
|
2006-07-05 09:46:42 +08:00
|
|
|
one->object.flags |= PARENT1;
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
if (!n) {
|
|
|
|
commit_list_append(one, &result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
prio_queue_put(&queue, one);
|
|
|
|
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
twos[i]->object.flags |= PARENT2;
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
prio_queue_put(&queue, twos[i]);
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
}
|
2006-06-29 21:17:32 +08:00
|
|
|
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
while (queue_has_nonstale(&queue)) {
|
|
|
|
struct commit *commit = prio_queue_get(&queue);
|
2006-06-29 21:17:32 +08:00
|
|
|
struct commit_list *parents;
|
2006-07-05 09:46:42 +08:00
|
|
|
int flags;
|
2006-06-29 21:17:32 +08:00
|
|
|
|
commit: don't use generation numbers if not needed
In 3afc679b "commit: use generations in paint_down_to_common()",
the queue in paint_down_to_common() was changed to use a priority
order based on generation number before commit date. This served
two purposes:
1. When generation numbers are present, the walk guarantees
correct topological relationships, regardless of clock skew in
commit dates.
2. It enables short-circuiting the walk when the min_generation
parameter is added in d7c1ec3e "commit: add short-circuit to
paint_down_to_common()". This short-circuit helps commands
like 'git branch --contains' from needing to walk to a merge
base when we know the result is false.
The commit message for 3afc679b includes the following sentence:
This change does not affect the number of commits that are
walked during the execution of paint_down_to_common(), only
the order that those commits are inspected.
This statement is incorrect. Because it changes the order in which
the commits are inspected, it changes the order they are added to
the queue, and hence can change the number of loops before the
queue_has_nonstale() method returns true.
This change makes a concrete difference depending on the topology
of the commit graph. For instance, computing the merge-base between
consecutive versions of the Linux kernel has no effect for versions
after v4.9, but 'git merge-base v4.8 v4.9' presents a performance
regression:
v2.18.0: 0.122s
v2.19.0-rc1: 0.547s
HEAD: 0.127s
To determine that this was simply an ordering issue, I inserted
a counter within the while loop of paint_down_to_common() and
found that the loop runs 167,468 times in v2.18.0 and 635,579
times in v2.19.0-rc1.
The topology of this case can be described in a simplified way
here:
v4.9
| \
| \
v4.8 \
| \ \
| \ |
... A B
| / /
| / /
|/__/
C
Here, the "..." means "a very long line of commits". By generation
number, A and B have generation one more than C. However, A and B
have commit date higher than most of the commits reachable from
v4.8. When the walk reaches v4.8, we realize that it has PARENT1
and PARENT2 flags, so everything it can reach is marked as STALE,
including A. B has only the PARENT1 flag, so is not STALE.
When paint_down_to_common() is run using
compare_commits_by_commit_date, A and B are removed from the queue
early and C is inserted into the queue. At this point, C and the
rest of the queue entries are marked as STALE. The loop then
terminates.
When paint_down_to_common() is run using
compare_commits_by_gen_then_commit_date, B is removed from the
queue only after the many commits reachable from v4.8 are explored.
This causes the loop to run longer. The reason for this regression
is simple: the queue order is intended to not explore a commit
until everything that _could_ reach that commit is explored. From
the information gathered by the original ordering, we have no
guarantee that there is not a commit D reachable from v4.8 that
can also reach B. We gained absolute correctness in exchange for
a performance regression.
The performance regression is probably the worse option, since
these incorrect results in paint_down_to_common() are rare. The
topology required for the performance regression are less rare,
but still require multiple merge commits where the parents differ
greatly in generation number. In our example above, the commit A
is as important as the commit B to demonstrate the problem, since
otherwise the commit C will sit in the queue as non-stale just as
long in both orders.
The solution provided uses the min_generation parameter to decide
if we should use generation numbers in our ordering. When
min_generation is equal to zero, it means that the caller has no
known cutoff for the walk, so we should rely on our commit-date
heuristic as before; this is the case with merge_bases_many().
When min_generation is non-zero, then the caller knows a valuable
cutoff for the short-circuit mechanism; this is the case with
remove_redundant() and in_merge_bases_many().
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-30 20:58:09 +08:00
|
|
|
if (min_generation && commit->generation > last_gen)
|
2018-05-01 20:47:19 +08:00
|
|
|
BUG("bad generation skip %8x > %8x at %s",
|
|
|
|
commit->generation, last_gen,
|
|
|
|
oid_to_hex(&commit->object.oid));
|
|
|
|
last_gen = commit->generation;
|
|
|
|
|
|
|
|
if (commit->generation < min_generation)
|
|
|
|
break;
|
|
|
|
|
2006-07-05 09:46:42 +08:00
|
|
|
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
|
|
|
|
if (flags == (PARENT1 | PARENT2)) {
|
|
|
|
if (!(commit->object.flags & RESULT)) {
|
|
|
|
commit->object.flags |= RESULT;
|
2010-11-27 09:58:14 +08:00
|
|
|
commit_list_insert_by_date(commit, &result);
|
2006-07-05 09:46:42 +08:00
|
|
|
}
|
2006-07-03 02:34:17 +08:00
|
|
|
/* Mark parents of a found merge stale */
|
|
|
|
flags |= STALE;
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
|
|
|
parents = commit->parents;
|
|
|
|
while (parents) {
|
|
|
|
struct commit *p = parents->item;
|
|
|
|
parents = parents->next;
|
|
|
|
if ((p->object.flags & flags) == flags)
|
|
|
|
continue;
|
2008-02-19 04:47:57 +08:00
|
|
|
if (parse_commit(p))
|
|
|
|
return NULL;
|
2006-06-29 21:17:32 +08:00
|
|
|
p->object.flags |= flags;
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
prio_queue_put(&queue, p);
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our
usual commit_list of commits to process, sorted by their
commit timestamp. As we add each parent to the list, we have
to spend "O(width of history)" to do the insertion, where
the width of history is the number of simultaneous lines of
development.
If we instead use a heap-based priority queue, we can do
these insertions in "O(log width)" time. This provides minor
speedups to merge-base calculations (timings in linux.git,
warm cache, best-of-five):
[before]
$ git merge-base HEAD v2.6.12
real 0m3.251s
user 0m3.148s
sys 0m0.104s
[after]
$ git merge-base HEAD v2.6.12
real 0m3.234s
user 0m3.108s
sys 0m0.128s
That's only an 0.5% speedup, but it does help protect us
against pathological cases.
While we are munging the "interesting" function, we also
take the opportunity to give it a more descriptive name, and
convert the return value to an int (we returned the first
interesting commit, but nobody ever looked at it).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-14 13:53:54 +08:00
|
|
|
clear_prio_queue(&queue);
|
2012-08-31 05:39:03 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
|
|
|
|
{
|
|
|
|
struct commit_list *list = NULL;
|
|
|
|
struct commit_list *result = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (one == twos[i])
|
|
|
|
/*
|
|
|
|
* We do not mark this even with RESULT so we do not
|
|
|
|
* have to clean it up.
|
|
|
|
*/
|
|
|
|
return commit_list_insert(one, &result);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parse_commit(one))
|
|
|
|
return NULL;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (parse_commit(twos[i]))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-05-01 20:47:19 +08:00
|
|
|
list = paint_down_to_common(one, n, twos, 0);
|
2012-08-31 05:39:03 +08:00
|
|
|
|
2006-07-05 09:46:42 +08:00
|
|
|
while (list) {
|
2015-10-25 00:21:31 +08:00
|
|
|
struct commit *commit = pop_commit(&list);
|
|
|
|
if (!(commit->object.flags & STALE))
|
|
|
|
commit_list_insert_by_date(commit, &result);
|
2006-07-05 09:46:42 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2006-06-29 21:17:32 +08:00
|
|
|
|
2008-06-28 00:22:00 +08:00
|
|
|
struct commit_list *get_octopus_merge_bases(struct commit_list *in)
|
|
|
|
{
|
|
|
|
struct commit_list *i, *j, *k, *ret = NULL;
|
|
|
|
|
2014-01-03 22:45:46 +08:00
|
|
|
if (!in)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
commit_list_insert(in->item, &ret);
|
|
|
|
|
|
|
|
for (i = in->next; i; i = i->next) {
|
2018-02-15 02:59:37 +08:00
|
|
|
struct commit_list *new_commits = NULL, *end = NULL;
|
2014-01-03 22:45:46 +08:00
|
|
|
|
|
|
|
for (j = ret; j; j = j->next) {
|
|
|
|
struct commit_list *bases;
|
2014-10-31 03:20:44 +08:00
|
|
|
bases = get_merge_bases(i->item, j->item);
|
2018-02-15 02:59:37 +08:00
|
|
|
if (!new_commits)
|
|
|
|
new_commits = bases;
|
2014-01-03 22:45:46 +08:00
|
|
|
else
|
|
|
|
end->next = bases;
|
|
|
|
for (k = bases; k; k = k->next)
|
|
|
|
end = k;
|
2008-06-28 00:22:00 +08:00
|
|
|
}
|
2018-02-15 02:59:37 +08:00
|
|
|
ret = new_commits;
|
2008-06-28 00:22:00 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-31 06:35:39 +08:00
|
|
|
static int remove_redundant(struct commit **array, int cnt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Some commit in the array may be an ancestor of
|
|
|
|
* another commit. Move such commit to the end of
|
|
|
|
* the array, and return the number of commits that
|
|
|
|
* are independent from each other.
|
|
|
|
*/
|
|
|
|
struct commit **work;
|
|
|
|
unsigned char *redundant;
|
|
|
|
int *filled_index;
|
|
|
|
int i, j, filled;
|
|
|
|
|
|
|
|
work = xcalloc(cnt, sizeof(*work));
|
|
|
|
redundant = xcalloc(cnt, 1);
|
2016-02-23 06:44:25 +08:00
|
|
|
ALLOC_ARRAY(filled_index, cnt - 1);
|
2012-08-31 06:35:39 +08:00
|
|
|
|
2012-10-05 06:37:15 +08:00
|
|
|
for (i = 0; i < cnt; i++)
|
|
|
|
parse_commit(array[i]);
|
2012-08-31 06:35:39 +08:00
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
struct commit_list *common;
|
2018-05-01 20:47:21 +08:00
|
|
|
uint32_t min_generation = array[i]->generation;
|
2012-08-31 06:35:39 +08:00
|
|
|
|
|
|
|
if (redundant[i])
|
|
|
|
continue;
|
|
|
|
for (j = filled = 0; j < cnt; j++) {
|
|
|
|
if (i == j || redundant[j])
|
|
|
|
continue;
|
|
|
|
filled_index[filled] = j;
|
|
|
|
work[filled++] = array[j];
|
2018-05-01 20:47:21 +08:00
|
|
|
|
|
|
|
if (array[j]->generation < min_generation)
|
|
|
|
min_generation = array[j]->generation;
|
2012-08-31 06:35:39 +08:00
|
|
|
}
|
2018-05-01 20:47:21 +08:00
|
|
|
common = paint_down_to_common(array[i], filled, work,
|
|
|
|
min_generation);
|
2012-08-31 06:35:39 +08:00
|
|
|
if (array[i]->object.flags & PARENT2)
|
|
|
|
redundant[i] = 1;
|
|
|
|
for (j = 0; j < filled; j++)
|
|
|
|
if (work[j]->object.flags & PARENT1)
|
|
|
|
redundant[filled_index[j]] = 1;
|
|
|
|
clear_commit_marks(array[i], all_flags);
|
2017-12-26 01:44:03 +08:00
|
|
|
clear_commit_marks_many(filled, work, all_flags);
|
2012-08-31 06:35:39 +08:00
|
|
|
free_commit_list(common);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now collect the result */
|
2016-09-25 15:24:03 +08:00
|
|
|
COPY_ARRAY(work, array, cnt);
|
2012-08-31 06:35:39 +08:00
|
|
|
for (i = filled = 0; i < cnt; i++)
|
|
|
|
if (!redundant[i])
|
|
|
|
array[filled++] = work[i];
|
|
|
|
for (j = filled, i = 0; i < cnt; i++)
|
|
|
|
if (redundant[i])
|
|
|
|
array[j++] = work[i];
|
|
|
|
free(work);
|
|
|
|
free(redundant);
|
|
|
|
free(filled_index);
|
|
|
|
return filled;
|
|
|
|
}
|
|
|
|
|
2014-10-31 03:20:44 +08:00
|
|
|
static struct commit_list *get_merge_bases_many_0(struct commit *one,
|
|
|
|
int n,
|
|
|
|
struct commit **twos,
|
|
|
|
int cleanup)
|
2006-07-05 09:46:42 +08:00
|
|
|
{
|
|
|
|
struct commit_list *list;
|
|
|
|
struct commit **rslt;
|
|
|
|
struct commit_list *result;
|
2012-08-31 06:35:39 +08:00
|
|
|
int cnt, i;
|
2006-07-05 09:46:42 +08:00
|
|
|
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
result = merge_bases_many(one, n, twos);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (one == twos[i])
|
|
|
|
return result;
|
|
|
|
}
|
2006-07-05 09:46:42 +08:00
|
|
|
if (!result || !result->next) {
|
|
|
|
if (cleanup) {
|
|
|
|
clear_commit_marks(one, all_flags);
|
2013-03-06 03:42:20 +08:00
|
|
|
clear_commit_marks_many(n, twos, all_flags);
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
2006-07-05 09:46:42 +08:00
|
|
|
return result;
|
2006-06-29 21:17:32 +08:00
|
|
|
}
|
|
|
|
|
2006-07-05 09:46:42 +08:00
|
|
|
/* There are more than one */
|
2014-07-17 07:52:09 +08:00
|
|
|
cnt = commit_list_count(result);
|
2006-07-05 09:46:42 +08:00
|
|
|
rslt = xcalloc(cnt, sizeof(*rslt));
|
|
|
|
for (list = result, i = 0; list; list = list->next)
|
|
|
|
rslt[i++] = list->item;
|
|
|
|
free_commit_list(result);
|
|
|
|
|
|
|
|
clear_commit_marks(one, all_flags);
|
2013-03-06 03:42:20 +08:00
|
|
|
clear_commit_marks_many(n, twos, all_flags);
|
2006-06-29 21:17:32 +08:00
|
|
|
|
2012-08-31 06:35:39 +08:00
|
|
|
cnt = remove_redundant(rslt, cnt);
|
2006-07-05 09:46:42 +08:00
|
|
|
result = NULL;
|
2012-08-31 06:35:39 +08:00
|
|
|
for (i = 0; i < cnt; i++)
|
|
|
|
commit_list_insert_by_date(rslt[i], &result);
|
2006-07-05 09:46:42 +08:00
|
|
|
free(rslt);
|
2006-06-29 21:17:32 +08:00
|
|
|
return result;
|
|
|
|
}
|
2006-12-19 16:14:04 +08:00
|
|
|
|
2014-10-31 03:20:44 +08:00
|
|
|
struct commit_list *get_merge_bases_many(struct commit *one,
|
|
|
|
int n,
|
|
|
|
struct commit **twos)
|
|
|
|
{
|
|
|
|
return get_merge_bases_many_0(one, n, twos, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct commit_list *get_merge_bases_many_dirty(struct commit *one,
|
|
|
|
int n,
|
|
|
|
struct commit **twos)
|
|
|
|
{
|
|
|
|
return get_merge_bases_many_0(one, n, twos, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
{
|
2014-10-31 03:20:44 +08:00
|
|
|
return get_merge_bases_many_0(one, 1, &two, 1);
|
Introduce get_merge_bases_many()
This introduces a new function get_merge_bases_many() which is a natural
extension of two commit merge base computation. It is given one commit
(one) and a set of other commits (twos), and computes the merge base of
one and a merge across other commits.
This is mostly useful to figure out the common ancestor when iterating
over heads during an octopus merge. When making an octopus between
commits A, B, C and D, we first merge tree of A and B, and then try to
merge C with it. If we were making pairwise merge, we would be recording
the tree resulting from the merge between A and B as a commit, say M, and
then the next round we will be computing the merge base between M and C.
o---C...*
/ .
o---B...M
/ .
o---o---A
But during an octopus merge, we actually do not create a commit M. In
order to figure out that the common ancestor to use for this merge,
instead of computing the merge base between C and M, we can call
merge_bases_many() with one set to C and twos containing A and B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-28 00:22:02 +08:00
|
|
|
}
|
|
|
|
|
2012-08-28 05:46:01 +08:00
|
|
|
/*
|
2013-04-12 06:36:10 +08:00
|
|
|
* Is "commit" a descendant of one of the elements on the "with_commit" list?
|
2012-08-28 05:46:01 +08:00
|
|
|
*/
|
2009-01-26 22:13:24 +08:00
|
|
|
int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
|
|
|
|
{
|
|
|
|
if (!with_commit)
|
|
|
|
return 1;
|
|
|
|
while (with_commit) {
|
|
|
|
struct commit *other;
|
|
|
|
|
|
|
|
other = with_commit->item;
|
|
|
|
with_commit = with_commit->next;
|
2012-08-28 05:46:01 +08:00
|
|
|
if (in_merge_bases(other, commit))
|
2009-01-26 22:13:24 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-28 05:46:01 +08:00
|
|
|
/*
|
2013-03-05 02:16:42 +08:00
|
|
|
* Is "commit" an ancestor of one of the "references"?
|
2012-08-28 05:46:01 +08:00
|
|
|
*/
|
2013-03-05 02:16:42 +08:00
|
|
|
int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
|
2006-12-19 16:14:04 +08:00
|
|
|
{
|
2012-08-31 06:04:13 +08:00
|
|
|
struct commit_list *bases;
|
2013-03-05 02:16:42 +08:00
|
|
|
int ret = 0, i;
|
2018-05-01 20:47:17 +08:00
|
|
|
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
|
2006-12-19 16:14:04 +08:00
|
|
|
|
2013-03-05 02:16:42 +08:00
|
|
|
if (parse_commit(commit))
|
2012-08-31 06:04:13 +08:00
|
|
|
return ret;
|
2018-05-01 20:47:17 +08:00
|
|
|
for (i = 0; i < nr_reference; i++) {
|
2013-03-05 02:16:42 +08:00
|
|
|
if (parse_commit(reference[i]))
|
|
|
|
return ret;
|
2018-05-01 20:47:17 +08:00
|
|
|
if (reference[i]->generation < min_generation)
|
|
|
|
min_generation = reference[i]->generation;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (commit->generation > min_generation)
|
|
|
|
return ret;
|
2006-12-19 16:14:04 +08:00
|
|
|
|
2018-05-01 20:47:19 +08:00
|
|
|
bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
|
2012-08-31 06:04:13 +08:00
|
|
|
if (commit->object.flags & PARENT2)
|
|
|
|
ret = 1;
|
2012-08-23 22:20:41 +08:00
|
|
|
clear_commit_marks(commit, all_flags);
|
2013-03-06 03:56:03 +08:00
|
|
|
clear_commit_marks_many(nr_reference, reference, all_flags);
|
2006-12-19 16:14:04 +08:00
|
|
|
free_commit_list(bases);
|
|
|
|
return ret;
|
|
|
|
}
|
2008-06-28 00:22:03 +08:00
|
|
|
|
2013-03-05 02:16:42 +08:00
|
|
|
/*
|
|
|
|
* Is "commit" an ancestor of (i.e. reachable from) the "reference"?
|
|
|
|
*/
|
|
|
|
int in_merge_bases(struct commit *commit, struct commit *reference)
|
|
|
|
{
|
|
|
|
return in_merge_bases_many(commit, 1, &reference);
|
|
|
|
}
|
|
|
|
|
2008-06-28 00:22:03 +08:00
|
|
|
struct commit_list *reduce_heads(struct commit_list *heads)
|
|
|
|
{
|
|
|
|
struct commit_list *p;
|
|
|
|
struct commit_list *result = NULL, **tail = &result;
|
2012-08-31 14:20:40 +08:00
|
|
|
struct commit **array;
|
|
|
|
int num_head, i;
|
2008-06-28 00:22:03 +08:00
|
|
|
|
|
|
|
if (!heads)
|
|
|
|
return NULL;
|
|
|
|
|
2012-08-31 14:20:40 +08:00
|
|
|
/* Uniquify */
|
|
|
|
for (p = heads; p; p = p->next)
|
|
|
|
p->item->object.flags &= ~STALE;
|
|
|
|
for (p = heads, num_head = 0; p; p = p->next) {
|
|
|
|
if (p->item->object.flags & STALE)
|
2008-07-14 15:09:41 +08:00
|
|
|
continue;
|
2012-08-31 14:20:40 +08:00
|
|
|
p->item->object.flags |= STALE;
|
|
|
|
num_head++;
|
|
|
|
}
|
2014-05-26 23:33:45 +08:00
|
|
|
array = xcalloc(num_head, sizeof(*array));
|
2012-08-31 14:20:40 +08:00
|
|
|
for (p = heads, i = 0; p; p = p->next) {
|
|
|
|
if (p->item->object.flags & STALE) {
|
|
|
|
array[i++] = p->item;
|
|
|
|
p->item->object.flags &= ~STALE;
|
2008-06-28 00:22:03 +08:00
|
|
|
}
|
|
|
|
}
|
2012-08-31 14:20:40 +08:00
|
|
|
num_head = remove_redundant(array, num_head);
|
|
|
|
for (i = 0; i < num_head; i++)
|
|
|
|
tail = &commit_list_insert(array[i], tail)->next;
|
2017-09-23 07:34:50 +08:00
|
|
|
free(array);
|
2008-06-28 00:22:03 +08:00
|
|
|
return result;
|
|
|
|
}
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
2017-11-08 04:39:45 +08:00
|
|
|
void reduce_heads_replace(struct commit_list **heads)
|
|
|
|
{
|
|
|
|
struct commit_list *result = reduce_heads(*heads);
|
|
|
|
free_commit_list(*heads);
|
|
|
|
*heads = result;
|
|
|
|
}
|
|
|
|
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
static const char gpg_sig_header[] = "gpgsig";
|
|
|
|
static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
|
|
|
|
|
|
|
|
static int do_sign_commit(struct strbuf *buf, const char *keyid)
|
|
|
|
{
|
|
|
|
struct strbuf sig = STRBUF_INIT;
|
|
|
|
int inspos, copypos;
|
2016-06-29 22:14:54 +08:00
|
|
|
const char *eoh;
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
|
|
|
|
/* find the end of the header */
|
2016-06-29 22:14:54 +08:00
|
|
|
eoh = strstr(buf->buf, "\n\n");
|
|
|
|
if (!eoh)
|
|
|
|
inspos = buf->len;
|
|
|
|
else
|
|
|
|
inspos = eoh - buf->buf + 1;
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
|
|
|
|
if (!keyid || !*keyid)
|
|
|
|
keyid = get_signing_key();
|
|
|
|
if (sign_buffer(buf, &sig, keyid)) {
|
|
|
|
strbuf_release(&sig);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (copypos = 0; sig.buf[copypos]; ) {
|
|
|
|
const char *bol = sig.buf + copypos;
|
|
|
|
const char *eol = strchrnul(bol, '\n');
|
|
|
|
int len = (eol - bol) + !!*eol;
|
|
|
|
|
|
|
|
if (!copypos) {
|
|
|
|
strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
|
|
|
|
inspos += gpg_sig_header_len;
|
|
|
|
}
|
|
|
|
strbuf_insert(buf, inspos++, " ", 1);
|
|
|
|
strbuf_insert(buf, inspos, bol, len);
|
|
|
|
inspos += len;
|
|
|
|
copypos += len;
|
|
|
|
}
|
|
|
|
strbuf_release(&sig);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
int parse_signed_commit(const struct commit *commit,
|
2011-10-19 06:53:23 +08:00
|
|
|
struct strbuf *payload, struct strbuf *signature)
|
|
|
|
{
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
|
2011-10-19 06:53:23 +08:00
|
|
|
unsigned long size;
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
const char *buffer = get_commit_buffer(commit, &size);
|
2011-10-19 06:53:23 +08:00
|
|
|
int in_signature, saw_signature = -1;
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
const char *line, *tail;
|
2011-10-19 06:53:23 +08:00
|
|
|
|
|
|
|
line = buffer;
|
|
|
|
tail = buffer + size;
|
|
|
|
in_signature = 0;
|
|
|
|
saw_signature = 0;
|
|
|
|
while (line < tail) {
|
|
|
|
const char *sig = NULL;
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
const char *next = memchr(line, '\n', tail - line);
|
2011-10-19 06:53:23 +08:00
|
|
|
|
|
|
|
next = next ? next + 1 : tail;
|
|
|
|
if (in_signature && line[0] == ' ')
|
|
|
|
sig = line + 1;
|
2013-12-01 04:55:40 +08:00
|
|
|
else if (starts_with(line, gpg_sig_header) &&
|
2011-10-19 06:53:23 +08:00
|
|
|
line[gpg_sig_header_len] == ' ')
|
|
|
|
sig = line + gpg_sig_header_len + 1;
|
|
|
|
if (sig) {
|
|
|
|
strbuf_add(signature, sig, next - sig);
|
|
|
|
saw_signature = 1;
|
|
|
|
in_signature = 1;
|
|
|
|
} else {
|
|
|
|
if (*line == '\n')
|
|
|
|
/* dump the whole remainder of the buffer */
|
|
|
|
next = tail;
|
|
|
|
strbuf_add(payload, line, next - line);
|
|
|
|
in_signature = 0;
|
|
|
|
}
|
|
|
|
line = next;
|
|
|
|
}
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
unuse_commit_buffer(commit, buffer);
|
2011-10-19 06:53:23 +08:00
|
|
|
return saw_signature;
|
|
|
|
}
|
|
|
|
|
2014-07-19 23:01:12 +08:00
|
|
|
int remove_signature(struct strbuf *buf)
|
|
|
|
{
|
|
|
|
const char *line = buf->buf;
|
|
|
|
const char *tail = buf->buf + buf->len;
|
|
|
|
int in_signature = 0;
|
|
|
|
const char *sig_start = NULL;
|
|
|
|
const char *sig_end = NULL;
|
|
|
|
|
|
|
|
while (line < tail) {
|
|
|
|
const char *next = memchr(line, '\n', tail - line);
|
|
|
|
next = next ? next + 1 : tail;
|
|
|
|
|
|
|
|
if (in_signature && line[0] == ' ')
|
|
|
|
sig_end = next;
|
|
|
|
else if (starts_with(line, gpg_sig_header) &&
|
|
|
|
line[gpg_sig_header_len] == ' ') {
|
|
|
|
sig_start = line;
|
|
|
|
sig_end = next;
|
|
|
|
in_signature = 1;
|
|
|
|
} else {
|
|
|
|
if (*line == '\n')
|
|
|
|
/* dump the whole remainder of the buffer */
|
|
|
|
next = tail;
|
|
|
|
in_signature = 0;
|
|
|
|
}
|
|
|
|
line = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sig_start)
|
|
|
|
strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
|
|
|
|
|
|
|
|
return sig_start != NULL;
|
|
|
|
}
|
|
|
|
|
2011-11-08 08:21:32 +08:00
|
|
|
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
|
|
|
|
{
|
|
|
|
struct merge_remote_desc *desc;
|
|
|
|
struct commit_extra_header *mergetag;
|
|
|
|
char *buf;
|
|
|
|
unsigned long size, len;
|
|
|
|
enum object_type type;
|
|
|
|
|
|
|
|
desc = merge_remote_util(parent);
|
|
|
|
if (!desc || !desc->obj)
|
|
|
|
return;
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:53 +08:00
|
|
|
buf = read_object_file(&desc->obj->oid, &type, &size);
|
2011-11-08 08:21:32 +08:00
|
|
|
if (!buf || type != OBJ_TAG)
|
|
|
|
goto free_return;
|
|
|
|
len = parse_signature(buf, size);
|
|
|
|
if (size == len)
|
|
|
|
goto free_return;
|
|
|
|
/*
|
|
|
|
* We could verify this signature and either omit the tag when
|
|
|
|
* it does not validate, but the integrator may not have the
|
|
|
|
* public key of the signer of the tag he is merging, while a
|
|
|
|
* later auditor may have it while auditing, so let's not run
|
|
|
|
* verify-signed-buffer here for now...
|
|
|
|
*
|
|
|
|
* if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
|
|
|
|
* warn("warning: signed tag unverified.");
|
|
|
|
*/
|
|
|
|
mergetag = xcalloc(1, sizeof(*mergetag));
|
|
|
|
mergetag->key = xstrdup("mergetag");
|
|
|
|
mergetag->value = buf;
|
|
|
|
mergetag->len = size;
|
|
|
|
|
|
|
|
**tail = mergetag;
|
|
|
|
*tail = &mergetag->next;
|
|
|
|
return;
|
|
|
|
|
|
|
|
free_return:
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
2015-06-22 07:14:40 +08:00
|
|
|
int check_commit_signature(const struct commit *commit, struct signature_check *sigc)
|
2013-04-01 00:00:14 +08:00
|
|
|
{
|
|
|
|
struct strbuf payload = STRBUF_INIT;
|
|
|
|
struct strbuf signature = STRBUF_INIT;
|
2015-06-22 07:14:40 +08:00
|
|
|
int ret = 1;
|
2013-04-01 00:00:14 +08:00
|
|
|
|
|
|
|
sigc->result = 'N';
|
|
|
|
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
if (parse_signed_commit(commit, &payload, &signature) <= 0)
|
2013-04-01 00:00:14 +08:00
|
|
|
goto out;
|
2015-06-22 07:14:40 +08:00
|
|
|
ret = check_signature(payload.buf, payload.len, signature.buf,
|
|
|
|
signature.len, sigc);
|
2013-04-01 00:00:14 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
strbuf_release(&payload);
|
|
|
|
strbuf_release(&signature);
|
2015-06-22 07:14:40 +08:00
|
|
|
|
|
|
|
return ret;
|
2013-04-01 00:00:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-11-08 08:21:32 +08:00
|
|
|
void append_merge_tag_headers(struct commit_list *parents,
|
|
|
|
struct commit_extra_header ***tail)
|
|
|
|
{
|
|
|
|
while (parents) {
|
|
|
|
struct commit *parent = parents->item;
|
|
|
|
handle_signed_tag(parent, tail);
|
|
|
|
parents = parents->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_extra_header(struct strbuf *buffer,
|
|
|
|
struct commit_extra_header *extra)
|
|
|
|
{
|
|
|
|
strbuf_addstr(buffer, extra->key);
|
2011-11-09 07:38:07 +08:00
|
|
|
if (extra->len)
|
|
|
|
strbuf_add_lines(buffer, " ", extra->value, extra->len);
|
|
|
|
else
|
|
|
|
strbuf_addch(buffer, '\n');
|
|
|
|
}
|
|
|
|
|
2012-01-06 02:54:14 +08:00
|
|
|
struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
|
|
|
|
const char **exclude)
|
2011-11-09 07:38:07 +08:00
|
|
|
{
|
|
|
|
struct commit_extra_header *extra = NULL;
|
|
|
|
unsigned long size;
|
reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).
However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:
[baseline, no signatures]
$ time git log >/dev/null
real 0m4.902s
user 0m4.784s
sys 0m0.120s
[before]
$ time git log --show-signature >/dev/null
real 0m14.735s
user 0m9.964s
sys 0m0.944s
[after]
$ time git log --show-signature >/dev/null
real 0m9.981s
user 0m5.260s
sys 0m0.936s
Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.
An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 14:32:11 +08:00
|
|
|
const char *buffer = get_commit_buffer(commit, &size);
|
|
|
|
extra = read_commit_extra_header_lines(buffer, size, exclude);
|
|
|
|
unuse_commit_buffer(commit, buffer);
|
2011-11-09 07:38:07 +08:00
|
|
|
return extra;
|
|
|
|
}
|
|
|
|
|
2018-04-25 17:54:04 +08:00
|
|
|
int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
|
2014-07-07 14:35:37 +08:00
|
|
|
{
|
|
|
|
struct commit_extra_header *extra, *to_free;
|
2018-04-25 17:54:04 +08:00
|
|
|
int res = 0;
|
2014-07-07 14:35:37 +08:00
|
|
|
|
|
|
|
to_free = read_commit_extra_headers(commit, NULL);
|
2018-04-25 17:54:04 +08:00
|
|
|
for (extra = to_free; !res && extra; extra = extra->next) {
|
2014-07-07 14:35:37 +08:00
|
|
|
if (strcmp(extra->key, "mergetag"))
|
|
|
|
continue; /* not a merge tag */
|
2018-04-25 17:54:04 +08:00
|
|
|
res = fn(commit, extra, data);
|
2014-07-07 14:35:37 +08:00
|
|
|
}
|
|
|
|
free_commit_extra_headers(to_free);
|
2018-04-25 17:54:04 +08:00
|
|
|
return res;
|
2014-07-07 14:35:37 +08:00
|
|
|
}
|
|
|
|
|
2011-11-09 07:38:07 +08:00
|
|
|
static inline int standard_header_field(const char *field, size_t len)
|
|
|
|
{
|
2017-02-26 03:27:40 +08:00
|
|
|
return ((len == 4 && !memcmp(field, "tree", 4)) ||
|
|
|
|
(len == 6 && !memcmp(field, "parent", 6)) ||
|
|
|
|
(len == 6 && !memcmp(field, "author", 6)) ||
|
|
|
|
(len == 9 && !memcmp(field, "committer", 9)) ||
|
|
|
|
(len == 8 && !memcmp(field, "encoding", 8)));
|
2011-11-09 07:38:07 +08:00
|
|
|
}
|
|
|
|
|
2012-01-06 02:54:14 +08:00
|
|
|
static int excluded_header_field(const char *field, size_t len, const char **exclude)
|
|
|
|
{
|
|
|
|
if (!exclude)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (*exclude) {
|
|
|
|
size_t xlen = strlen(*exclude);
|
2017-02-26 03:27:40 +08:00
|
|
|
if (len == xlen && !memcmp(field, *exclude, xlen))
|
2012-01-06 02:54:14 +08:00
|
|
|
return 1;
|
|
|
|
exclude++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-16 04:58:15 +08:00
|
|
|
static struct commit_extra_header *read_commit_extra_header_lines(
|
|
|
|
const char *buffer, size_t size,
|
|
|
|
const char **exclude)
|
2011-11-09 07:38:07 +08:00
|
|
|
{
|
|
|
|
struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
|
|
|
|
const char *line, *next, *eof, *eob;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
|
|
|
for (line = buffer, eob = line + size;
|
|
|
|
line < eob && *line != '\n';
|
|
|
|
line = next) {
|
|
|
|
next = memchr(line, '\n', eob - line);
|
|
|
|
next = next ? next + 1 : eob;
|
|
|
|
if (*line == ' ') {
|
|
|
|
/* continuation */
|
|
|
|
if (it)
|
|
|
|
strbuf_add(&buf, line + 1, next - (line + 1));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (it)
|
|
|
|
it->value = strbuf_detach(&buf, &it->len);
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
it = NULL;
|
|
|
|
|
2017-02-26 03:21:52 +08:00
|
|
|
eof = memchr(line, ' ', next - line);
|
|
|
|
if (!eof)
|
2011-11-09 07:38:07 +08:00
|
|
|
eof = next;
|
2017-02-26 03:27:40 +08:00
|
|
|
else if (standard_header_field(line, eof - line) ||
|
|
|
|
excluded_header_field(line, eof - line, exclude))
|
2011-11-09 07:38:07 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
it = xcalloc(1, sizeof(*it));
|
|
|
|
it->key = xmemdupz(line, eof-line);
|
|
|
|
*tail = it;
|
|
|
|
tail = &it->next;
|
|
|
|
if (eof + 1 < next)
|
|
|
|
strbuf_add(&buf, eof + 1, next - (eof + 1));
|
|
|
|
}
|
|
|
|
if (it)
|
|
|
|
it->value = strbuf_detach(&buf, &it->len);
|
|
|
|
return extra;
|
2011-11-08 08:21:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void free_commit_extra_headers(struct commit_extra_header *extra)
|
|
|
|
{
|
|
|
|
while (extra) {
|
|
|
|
struct commit_extra_header *next = extra->next;
|
|
|
|
free(extra->key);
|
|
|
|
free(extra->value);
|
|
|
|
free(extra);
|
|
|
|
extra = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-28 08:13:16 +08:00
|
|
|
int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
|
|
|
|
struct commit_list *parents, struct object_id *ret,
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
const char *author, const char *sign_commit)
|
2011-11-08 08:21:32 +08:00
|
|
|
{
|
|
|
|
struct commit_extra_header *extra = NULL, **tail = &extra;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
append_merge_tag_headers(parents, &tail);
|
2014-06-11 05:36:52 +08:00
|
|
|
result = commit_tree_extended(msg, msg_len, tree, parents, ret,
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
author, sign_commit, extra);
|
2011-11-08 08:21:32 +08:00
|
|
|
free_commit_extra_headers(extra);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-06-29 02:24:14 +08:00
|
|
|
static int find_invalid_utf8(const char *buf, int len)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
2013-07-05 01:20:34 +08:00
|
|
|
static const unsigned int max_codepoint[] = {
|
|
|
|
0x7f, 0x7ff, 0xffff, 0x10ffff
|
|
|
|
};
|
2012-06-29 02:24:14 +08:00
|
|
|
|
|
|
|
while (len) {
|
|
|
|
unsigned char c = *buf++;
|
|
|
|
int bytes, bad_offset;
|
2013-07-05 01:19:43 +08:00
|
|
|
unsigned int codepoint;
|
2013-07-05 01:20:34 +08:00
|
|
|
unsigned int min_val, max_val;
|
2012-06-29 02:24:14 +08:00
|
|
|
|
|
|
|
len--;
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
/* Simple US-ASCII? No worries. */
|
|
|
|
if (c < 0x80)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bad_offset = offset-1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count how many more high bits set: that's how
|
|
|
|
* many more bytes this sequence should have.
|
|
|
|
*/
|
|
|
|
bytes = 0;
|
|
|
|
while (c & 0x40) {
|
|
|
|
c <<= 1;
|
|
|
|
bytes++;
|
|
|
|
}
|
|
|
|
|
2013-07-05 01:19:43 +08:00
|
|
|
/*
|
|
|
|
* Must be between 1 and 3 more bytes. Longer sequences result in
|
|
|
|
* codepoints beyond U+10FFFF, which are guaranteed never to exist.
|
|
|
|
*/
|
|
|
|
if (bytes < 1 || 3 < bytes)
|
2012-06-29 02:24:14 +08:00
|
|
|
return bad_offset;
|
|
|
|
|
|
|
|
/* Do we *have* that many bytes? */
|
|
|
|
if (len < bytes)
|
|
|
|
return bad_offset;
|
|
|
|
|
2013-07-05 01:20:34 +08:00
|
|
|
/*
|
|
|
|
* Place the encoded bits at the bottom of the value and compute the
|
|
|
|
* valid range.
|
|
|
|
*/
|
2013-07-05 01:19:43 +08:00
|
|
|
codepoint = (c & 0x7f) >> bytes;
|
2013-07-05 01:20:34 +08:00
|
|
|
min_val = max_codepoint[bytes-1] + 1;
|
|
|
|
max_val = max_codepoint[bytes];
|
2013-07-05 01:19:43 +08:00
|
|
|
|
2012-06-29 02:24:14 +08:00
|
|
|
offset += bytes;
|
|
|
|
len -= bytes;
|
|
|
|
|
|
|
|
/* And verify that they are good continuation bytes */
|
|
|
|
do {
|
2013-07-05 01:19:43 +08:00
|
|
|
codepoint <<= 6;
|
|
|
|
codepoint |= *buf & 0x3f;
|
2012-06-29 02:24:14 +08:00
|
|
|
if ((*buf++ & 0xc0) != 0x80)
|
|
|
|
return bad_offset;
|
|
|
|
} while (--bytes);
|
|
|
|
|
2013-07-05 01:20:34 +08:00
|
|
|
/* Reject codepoints that are out of range for the sequence length. */
|
|
|
|
if (codepoint < min_val || codepoint > max_val)
|
2013-07-05 01:19:43 +08:00
|
|
|
return bad_offset;
|
|
|
|
/* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */
|
|
|
|
if ((codepoint & 0x1ff800) == 0xd800)
|
|
|
|
return bad_offset;
|
2013-07-09 19:16:33 +08:00
|
|
|
/* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */
|
2013-08-06 00:52:28 +08:00
|
|
|
if ((codepoint & 0xfffe) == 0xfffe)
|
2013-07-09 19:16:33 +08:00
|
|
|
return bad_offset;
|
|
|
|
/* So are anything in the range U+FDD0..U+FDEF. */
|
|
|
|
if (codepoint >= 0xfdd0 && codepoint <= 0xfdef)
|
2013-07-05 01:19:43 +08:00
|
|
|
return bad_offset;
|
2012-06-29 02:24:14 +08:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This verifies that the buffer is in proper utf8 format.
|
|
|
|
*
|
|
|
|
* If it isn't, it assumes any non-utf8 characters are Latin1,
|
|
|
|
* and does the conversion.
|
|
|
|
*/
|
|
|
|
static int verify_utf8(struct strbuf *buf)
|
|
|
|
{
|
|
|
|
int ok = 1;
|
|
|
|
long pos = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int bad;
|
|
|
|
unsigned char c;
|
|
|
|
unsigned char replace[2];
|
|
|
|
|
|
|
|
bad = find_invalid_utf8(buf->buf + pos, buf->len - pos);
|
|
|
|
if (bad < 0)
|
|
|
|
return ok;
|
|
|
|
pos += bad;
|
|
|
|
ok = 0;
|
|
|
|
c = buf->buf[pos];
|
|
|
|
strbuf_remove(buf, pos, 1);
|
|
|
|
|
|
|
|
/* We know 'c' must be in the range 128-255 */
|
|
|
|
replace[0] = 0xc0 + (c >> 6);
|
|
|
|
replace[1] = 0x80 + (c & 0x3f);
|
|
|
|
strbuf_insert(buf, pos, replace, 2);
|
|
|
|
pos += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
static const char commit_utf8_warn[] =
|
2016-09-19 21:08:16 +08:00
|
|
|
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");
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
2014-06-11 05:36:52 +08:00
|
|
|
int commit_tree_extended(const char *msg, size_t msg_len,
|
2018-01-28 08:13:16 +08:00
|
|
|
const struct object_id *tree,
|
|
|
|
struct commit_list *parents, struct object_id *ret,
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
const char *author, const char *sign_commit,
|
|
|
|
struct commit_extra_header *extra)
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
int encoding_is_utf8;
|
|
|
|
struct strbuf buffer;
|
|
|
|
|
2018-03-12 10:27:42 +08:00
|
|
|
assert_oid_type(tree, OBJ_TREE);
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
2014-06-11 05:36:52 +08:00
|
|
|
if (memchr(msg, '\0', msg_len))
|
2011-12-15 21:47:23 +08:00
|
|
|
return error("a NUL byte in commit log message not allowed.");
|
|
|
|
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
/* Not having i18n.commitencoding is the same as having utf-8 */
|
|
|
|
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
|
|
|
|
|
|
|
|
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
|
2018-01-28 08:13:16 +08:00
|
|
|
strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE! This ordering means that the same exact tree merged with a
|
|
|
|
* different order of parents will be a _different_ changeset even
|
|
|
|
* if everything else stays the same.
|
|
|
|
*/
|
|
|
|
while (parents) {
|
2015-10-25 00:21:31 +08:00
|
|
|
struct commit *parent = pop_commit(&parents);
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
strbuf_addf(&buffer, "parent %s\n",
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&parent->object.oid));
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Person/date information */
|
|
|
|
if (!author)
|
2012-05-25 07:28:40 +08:00
|
|
|
author = git_author_info(IDENT_STRICT);
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
strbuf_addf(&buffer, "author %s\n", author);
|
2012-05-25 07:28:40 +08:00
|
|
|
strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
if (!encoding_is_utf8)
|
|
|
|
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
|
2011-11-08 08:21:32 +08:00
|
|
|
|
|
|
|
while (extra) {
|
|
|
|
add_extra_header(&buffer, extra);
|
|
|
|
extra = extra->next;
|
|
|
|
}
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
strbuf_addch(&buffer, '\n');
|
|
|
|
|
|
|
|
/* And add the comment */
|
2014-06-11 05:36:52 +08:00
|
|
|
strbuf_add(&buffer, msg, msg_len);
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
|
|
|
/* And check the encoding */
|
2012-06-29 02:24:14 +08:00
|
|
|
if (encoding_is_utf8 && !verify_utf8(&buffer))
|
2016-09-19 21:08:16 +08:00
|
|
|
fprintf(stderr, _(commit_utf8_warn));
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
|
2017-08-31 01:49:38 +08:00
|
|
|
if (sign_commit && do_sign_commit(&buffer, sign_commit)) {
|
|
|
|
result = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e.
$ git commit --gpg-sign -m foo
You need a passphrase to unlock the secret key for
user: "Junio C Hamano <gitster@pobox.com>"
4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)
[master 8457d13] foo
1 files changed, 1 insertions(+), 0 deletions(-)
The lines of GPG detached signature are placed in a new multi-line header
field, instead of tucking the signature block at the end of the commit log
message text (similar to how signed tag is done), for multiple reasons:
- The signature won't clutter output from "git log" and friends if it is
in the extra header. If we place it at the end of the log message, we
would need to teach "git log" and friends to strip the signature block
with an option.
- Teaching new versions of "git log" and "gitk" to optionally verify and
show signatures is cleaner if we structurally know where the signature
block is (instead of scanning in the commit log message).
- The signature needs to be stripped upon various commit rewriting
operations, e.g. rebase, filter-branch, etc. They all already ignore
unknown headers, but if we place signature in the log message, all of
these tools (and third-party tools) also need to learn how a signature
block would look like.
- When we added the optional encoding header, all the tools (both in tree
and third-party) that acts on the raw commit object should have been
fixed to ignore headers they do not understand, so it is not like that
new header would be more likely to break than extra text in the commit.
A commit made with the above sample sequence would look like this:
$ git cat-file commit HEAD
tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
...
=dt98
-----END PGP SIGNATURE-----
foo
but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-10-06 08:23:20 +08:00
|
|
|
|
2018-01-28 08:13:19 +08:00
|
|
|
result = write_object_file(buffer.buf, buffer.len, commit_type, ret);
|
2017-08-31 01:49:38 +08:00
|
|
|
out:
|
make commit_tree a library function
Until now, this has been part of the commit-tree builtin.
However, it is already used by other builtins (like commit,
merge, and notes), and it would be useful to access it from
library code.
The check_valid helper has to come along, too, but is given
a more library-ish name of "assert_sha1_type".
Otherwise, the code is unchanged. There are still a few
rough edges for a library function, like printing the utf8
warning to stderr, but we can address those if and when they
come up as inappropriate.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-04-02 08:05:23 +08:00
|
|
|
strbuf_release(&buffer);
|
|
|
|
return result;
|
|
|
|
}
|
2011-11-08 05:26:22 +08:00
|
|
|
|
2018-05-19 13:28:30 +08:00
|
|
|
define_commit_slab(merge_desc_slab, struct merge_remote_desc *);
|
|
|
|
static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab);
|
|
|
|
|
|
|
|
struct merge_remote_desc *merge_remote_util(struct commit *commit)
|
|
|
|
{
|
|
|
|
return *merge_desc_slab_at(&merge_desc_slab, commit);
|
|
|
|
}
|
|
|
|
|
2016-08-13 20:11:27 +08:00
|
|
|
void set_merge_remote_desc(struct commit *commit,
|
|
|
|
const char *name, struct object *obj)
|
|
|
|
{
|
|
|
|
struct merge_remote_desc *desc;
|
2016-08-13 20:21:27 +08:00
|
|
|
FLEX_ALLOC_STR(desc, name, name);
|
2016-08-13 20:11:27 +08:00
|
|
|
desc->obj = obj;
|
2018-05-19 13:28:30 +08:00
|
|
|
*merge_desc_slab_at(&merge_desc_slab, commit) = desc;
|
2016-08-13 20:11:27 +08:00
|
|
|
}
|
|
|
|
|
2011-11-08 05:26:22 +08:00
|
|
|
struct commit *get_merge_parent(const char *name)
|
|
|
|
{
|
|
|
|
struct object *obj;
|
|
|
|
struct commit *commit;
|
2015-03-14 07:39:34 +08:00
|
|
|
struct object_id oid;
|
sha1_name: convert get_sha1* to get_oid*
Now that all the callers of get_sha1 directly or indirectly use struct
object_id, rename the functions starting with get_sha1 to start with
get_oid. Convert the internals in sha1_name.c to use struct object_id
as well, and eliminate explicit length checks where possible. Convert a
use of 40 in get_oid_basic to GIT_SHA1_HEXSZ.
Outside of sha1_name.c and cache.h, this transition was made with the
following semantic patch:
@@
expression E1, E2;
@@
- get_sha1(E1, E2.hash)
+ get_oid(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1(E1, E2->hash)
+ get_oid(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2.hash)
+ get_oid_committish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2->hash)
+ get_oid_committish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2.hash)
+ get_oid_treeish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2->hash)
+ get_oid_treeish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2.hash)
+ get_oid_commit(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2->hash)
+ get_oid_commit(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2.hash)
+ get_oid_tree(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2->hash)
+ get_oid_tree(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2.hash)
+ get_oid_blob(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2->hash)
+ get_oid_blob(E1, E2)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3.hash, E4)
+ get_oid_with_context(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3->hash, E4)
+ get_oid_with_context(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-14 07:49:28 +08:00
|
|
|
if (get_oid(name, &oid))
|
2011-11-08 05:26:22 +08:00
|
|
|
return NULL;
|
2018-06-29 09:21:51 +08:00
|
|
|
obj = parse_object(the_repository, &oid);
|
2011-11-08 05:26:22 +08:00
|
|
|
commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
|
2018-05-19 13:28:30 +08:00
|
|
|
if (commit && !merge_remote_util(commit))
|
2016-08-13 20:11:27 +08:00
|
|
|
set_merge_remote_desc(commit, name, obj);
|
2011-11-08 05:26:22 +08:00
|
|
|
return commit;
|
|
|
|
}
|
2012-04-26 04:35:27 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Append a commit to the end of the commit_list.
|
|
|
|
*
|
|
|
|
* next starts by pointing to the variable that holds the head of an
|
|
|
|
* empty commit_list, and is updated to point to the "next" field of
|
|
|
|
* the last item on the list as new commits are appended.
|
|
|
|
*
|
|
|
|
* Usage example:
|
|
|
|
*
|
|
|
|
* struct commit_list *list;
|
|
|
|
* struct commit_list **next = &list;
|
|
|
|
*
|
|
|
|
* next = commit_list_append(c1, next);
|
|
|
|
* next = commit_list_append(c2, next);
|
|
|
|
* assert(commit_list_count(list) == 2);
|
|
|
|
* return list;
|
|
|
|
*/
|
|
|
|
struct commit_list **commit_list_append(struct commit *commit,
|
|
|
|
struct commit_list **next)
|
|
|
|
{
|
2018-02-15 02:59:37 +08:00
|
|
|
struct commit_list *new_commit = xmalloc(sizeof(struct commit_list));
|
|
|
|
new_commit->item = commit;
|
|
|
|
*next = new_commit;
|
|
|
|
new_commit->next = NULL;
|
|
|
|
return &new_commit->next;
|
2012-04-26 04:35:27 +08:00
|
|
|
}
|
2012-10-26 23:53:51 +08:00
|
|
|
|
commit: provide a function to find a header in a buffer
Usually when we parse a commit, we read it line by line and
handle each individual line (e.g., parse_commit and
parse_commit_header). Sometimes, however, we only care
about extracting a single header. Code in this situation is
stuck doing an ad-hoc parse of the commit buffer.
Let's provide a reusable function to locate a header within
the commit. The code is modeled after pretty.c's
get_header, which is used to extract the encoding.
Since some callers may not have the "struct commit" to go
along with the buffer, we drop that parameter. The only
thing lost is a warning for truncated commits, but that's
OK. This shouldn't happen in practice, and even if it does,
there's no particular reason that this function needs to
complain about it. It either finds the header it was asked
for, or it doesn't (and in the latter case, the caller will
typically complain).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-08-27 15:56:01 +08:00
|
|
|
const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
|
|
|
|
{
|
|
|
|
int key_len = strlen(key);
|
|
|
|
const char *line = msg;
|
|
|
|
|
|
|
|
while (line) {
|
|
|
|
const char *eol = strchrnul(line, '\n');
|
|
|
|
|
|
|
|
if (line == eol)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (eol - line > key_len &&
|
|
|
|
!strncmp(line, key, key_len) &&
|
|
|
|
line[key_len] == ' ') {
|
|
|
|
*out_len = eol - line - key_len - 1;
|
|
|
|
return line + key_len + 1;
|
|
|
|
}
|
|
|
|
line = *eol ? eol + 1 : NULL;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-23 04:26:23 +08:00
|
|
|
|
2014-11-09 17:23:41 +08:00
|
|
|
/*
|
2016-11-03 01:29:17 +08:00
|
|
|
* Inspect the given string and determine the true "end" of the log message, in
|
2014-11-09 17:23:41 +08:00
|
|
|
* order to find where to put a new Signed-off-by: line. Ignored are
|
interpret-trailers: honor the cut line
If a commit message is edited with the "verbose" option, the buffer
will have a cut line and diff after the log message, like so:
my subject
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/foo.txt b/foo.txt
index 5716ca5..7601807 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1 @@
-bar
+baz
"git interpret-trailers" is unaware of the cut line, and assumes the
trailer block would be at the end of the whole thing. This can easily
be seen with:
$ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \
git commit --amend -v
Teach "git interpret-trailers" to notice the cut-line and ignore the
remainder of the input when looking for a place to add new trailer
block. This makes it consistent with how "git commit -v -s" inserts a
new Signed-off-by: line.
This can be done by the same logic as the existing helper function,
wt_status_truncate_message_at_cut_line(), uses, but it wants the caller
to pass a strbuf to it. Because the function ignore_non_trailer() used
by the command takes a <pointer, length> pair, not a strbuf, steal the
logic from wt_status_truncate_message_at_cut_line() to create a new
wt_status_locate_end() helper function that takes <pointer, length>
pair, and make ignore_non_trailer() call it to help "interpret-trailers".
Since there is only one caller of wt_status_truncate_message_at_cut_line()
in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead
and remove the old helper that no longer has any caller.
Signed-off-by: Brian Malehorn <bmalehorn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-16 14:06:49 +08:00
|
|
|
* trailing comment lines and blank lines. To support "git commit -s
|
|
|
|
* --amend" on an existing commit, we also ignore "Conflicts:". To
|
|
|
|
* support "git commit -v", we truncate at cut lines.
|
2014-11-09 17:23:41 +08:00
|
|
|
*
|
|
|
|
* Returns the number of bytes from the tail to ignore, to be fed as
|
|
|
|
* the second parameter to append_signoff().
|
|
|
|
*/
|
2016-11-03 01:29:17 +08:00
|
|
|
int ignore_non_trailer(const char *buf, size_t len)
|
2014-11-09 17:23:41 +08:00
|
|
|
{
|
|
|
|
int boc = 0;
|
|
|
|
int bol = 0;
|
|
|
|
int in_old_conflicts_block = 0;
|
interpret-trailers: honor the cut line
If a commit message is edited with the "verbose" option, the buffer
will have a cut line and diff after the log message, like so:
my subject
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/foo.txt b/foo.txt
index 5716ca5..7601807 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1 @@
-bar
+baz
"git interpret-trailers" is unaware of the cut line, and assumes the
trailer block would be at the end of the whole thing. This can easily
be seen with:
$ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \
git commit --amend -v
Teach "git interpret-trailers" to notice the cut-line and ignore the
remainder of the input when looking for a place to add new trailer
block. This makes it consistent with how "git commit -v -s" inserts a
new Signed-off-by: line.
This can be done by the same logic as the existing helper function,
wt_status_truncate_message_at_cut_line(), uses, but it wants the caller
to pass a strbuf to it. Because the function ignore_non_trailer() used
by the command takes a <pointer, length> pair, not a strbuf, steal the
logic from wt_status_truncate_message_at_cut_line() to create a new
wt_status_locate_end() helper function that takes <pointer, length>
pair, and make ignore_non_trailer() call it to help "interpret-trailers".
Since there is only one caller of wt_status_truncate_message_at_cut_line()
in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead
and remove the old helper that no longer has any caller.
Signed-off-by: Brian Malehorn <bmalehorn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-16 14:06:49 +08:00
|
|
|
size_t cutoff = wt_status_locate_end(buf, len);
|
2014-11-09 17:23:41 +08:00
|
|
|
|
interpret-trailers: honor the cut line
If a commit message is edited with the "verbose" option, the buffer
will have a cut line and diff after the log message, like so:
my subject
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/foo.txt b/foo.txt
index 5716ca5..7601807 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1 @@
-bar
+baz
"git interpret-trailers" is unaware of the cut line, and assumes the
trailer block would be at the end of the whole thing. This can easily
be seen with:
$ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \
git commit --amend -v
Teach "git interpret-trailers" to notice the cut-line and ignore the
remainder of the input when looking for a place to add new trailer
block. This makes it consistent with how "git commit -v -s" inserts a
new Signed-off-by: line.
This can be done by the same logic as the existing helper function,
wt_status_truncate_message_at_cut_line(), uses, but it wants the caller
to pass a strbuf to it. Because the function ignore_non_trailer() used
by the command takes a <pointer, length> pair, not a strbuf, steal the
logic from wt_status_truncate_message_at_cut_line() to create a new
wt_status_locate_end() helper function that takes <pointer, length>
pair, and make ignore_non_trailer() call it to help "interpret-trailers".
Since there is only one caller of wt_status_truncate_message_at_cut_line()
in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead
and remove the old helper that no longer has any caller.
Signed-off-by: Brian Malehorn <bmalehorn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-16 14:06:49 +08:00
|
|
|
while (bol < cutoff) {
|
2016-11-03 01:29:17 +08:00
|
|
|
const char *next_line = memchr(buf + bol, '\n', len - bol);
|
2014-11-09 17:23:41 +08:00
|
|
|
|
2016-11-03 01:29:17 +08:00
|
|
|
if (!next_line)
|
|
|
|
next_line = buf + len;
|
2014-11-09 17:23:41 +08:00
|
|
|
else
|
|
|
|
next_line++;
|
|
|
|
|
2016-11-03 01:29:17 +08:00
|
|
|
if (buf[bol] == comment_line_char || buf[bol] == '\n') {
|
2014-11-09 17:23:41 +08:00
|
|
|
/* is this the first of the run of comments? */
|
|
|
|
if (!boc)
|
|
|
|
boc = bol;
|
|
|
|
/* otherwise, it is just continuing */
|
2016-11-03 01:29:17 +08:00
|
|
|
} else if (starts_with(buf + bol, "Conflicts:\n")) {
|
2014-11-09 17:23:41 +08:00
|
|
|
in_old_conflicts_block = 1;
|
|
|
|
if (!boc)
|
|
|
|
boc = bol;
|
2016-11-03 01:29:17 +08:00
|
|
|
} else if (in_old_conflicts_block && buf[bol] == '\t') {
|
2014-11-09 17:23:41 +08:00
|
|
|
; /* a pathname in the conflicts block */
|
|
|
|
} else if (boc) {
|
|
|
|
/* the previous was not trailing comment */
|
|
|
|
boc = 0;
|
|
|
|
in_old_conflicts_block = 0;
|
|
|
|
}
|
2016-11-03 01:29:17 +08:00
|
|
|
bol = next_line - buf;
|
2014-11-09 17:23:41 +08:00
|
|
|
}
|
interpret-trailers: honor the cut line
If a commit message is edited with the "verbose" option, the buffer
will have a cut line and diff after the log message, like so:
my subject
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/foo.txt b/foo.txt
index 5716ca5..7601807 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1 @@
-bar
+baz
"git interpret-trailers" is unaware of the cut line, and assumes the
trailer block would be at the end of the whole thing. This can easily
be seen with:
$ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \
git commit --amend -v
Teach "git interpret-trailers" to notice the cut-line and ignore the
remainder of the input when looking for a place to add new trailer
block. This makes it consistent with how "git commit -v -s" inserts a
new Signed-off-by: line.
This can be done by the same logic as the existing helper function,
wt_status_truncate_message_at_cut_line(), uses, but it wants the caller
to pass a strbuf to it. Because the function ignore_non_trailer() used
by the command takes a <pointer, length> pair, not a strbuf, steal the
logic from wt_status_truncate_message_at_cut_line() to create a new
wt_status_locate_end() helper function that takes <pointer, length>
pair, and make ignore_non_trailer() call it to help "interpret-trailers".
Since there is only one caller of wt_status_truncate_message_at_cut_line()
in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead
and remove the old helper that no longer has any caller.
Signed-off-by: Brian Malehorn <bmalehorn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-16 14:06:49 +08:00
|
|
|
return boc ? len - boc : len - cutoff;
|
2014-11-09 17:23:41 +08:00
|
|
|
}
|