2007-01-11 11:47:48 +01:00
|
|
|
#include "cache.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
2008-07-21 20:03:49 +02:00
|
|
|
#include "string-list.h"
|
2007-01-20 09:47:34 +01:00
|
|
|
#include "reflog-walk.h"
|
2007-01-11 11:47:48 +01:00
|
|
|
|
|
|
|
struct complete_reflogs {
|
|
|
|
char *ref;
|
2009-10-19 17:48:10 +02:00
|
|
|
const char *short_ref;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct reflog_info {
|
2017-02-22 00:47:31 +01:00
|
|
|
struct object_id ooid, noid;
|
2007-01-11 11:47:48 +01:00
|
|
|
char *email;
|
2017-04-26 21:29:31 +02:00
|
|
|
timestamp_t timestamp;
|
2007-01-11 11:47:48 +01:00
|
|
|
int tz;
|
|
|
|
char *message;
|
|
|
|
} *items;
|
|
|
|
int nr, alloc;
|
|
|
|
};
|
|
|
|
|
2017-02-22 00:47:32 +01:00
|
|
|
static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
|
2017-04-26 21:29:31 +02:00
|
|
|
const char *email, timestamp_t timestamp, int tz,
|
2007-01-11 11:47:48 +01:00
|
|
|
const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct complete_reflogs *array = cb_data;
|
|
|
|
struct reflog_info *item;
|
|
|
|
|
2014-03-03 23:31:57 +01:00
|
|
|
ALLOC_GROW(array->items, array->nr + 1, array->alloc);
|
2007-01-11 11:47:48 +01:00
|
|
|
item = array->items + array->nr;
|
2017-02-22 00:47:32 +01:00
|
|
|
oidcpy(&item->ooid, ooid);
|
|
|
|
oidcpy(&item->noid, noid);
|
2007-01-11 11:47:48 +01:00
|
|
|
item->email = xstrdup(email);
|
|
|
|
item->timestamp = timestamp;
|
|
|
|
item->tz = tz;
|
|
|
|
item->message = xstrdup(message);
|
|
|
|
array->nr++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-07 10:43:16 +02:00
|
|
|
static void free_complete_reflog(struct complete_reflogs *array)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!array)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < array->nr; i++) {
|
|
|
|
free(array->items[i].email);
|
|
|
|
free(array->items[i].message);
|
|
|
|
}
|
|
|
|
free(array->items);
|
|
|
|
free(array->ref);
|
|
|
|
free(array);
|
|
|
|
}
|
|
|
|
|
2007-01-11 11:47:48 +01:00
|
|
|
static struct complete_reflogs *read_complete_reflog(const char *ref)
|
|
|
|
{
|
|
|
|
struct complete_reflogs *reflogs =
|
2014-05-26 17:33:54 +02:00
|
|
|
xcalloc(1, sizeof(struct complete_reflogs));
|
2007-01-11 11:47:48 +01:00
|
|
|
reflogs->ref = xstrdup(ref);
|
|
|
|
for_each_reflog_ent(ref, read_one_reflog, reflogs);
|
|
|
|
if (reflogs->nr == 0) {
|
2017-02-22 00:47:29 +01:00
|
|
|
struct object_id oid;
|
2011-12-13 15:17:48 +01:00
|
|
|
const char *name;
|
|
|
|
void *name_to_free;
|
2014-07-15 21:59:36 +02:00
|
|
|
name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
|
2017-02-22 00:47:29 +01:00
|
|
|
oid.hash, NULL);
|
2011-11-13 11:22:15 +01:00
|
|
|
if (name) {
|
2007-01-11 11:47:48 +01:00
|
|
|
for_each_reflog_ent(name, read_one_reflog, reflogs);
|
2011-12-13 15:17:48 +01:00
|
|
|
free(name_to_free);
|
2011-11-13 11:22:15 +01:00
|
|
|
}
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
|
|
|
if (reflogs->nr == 0) {
|
2015-09-24 23:07:03 +02:00
|
|
|
char *refname = xstrfmt("refs/%s", ref);
|
2007-01-11 11:47:48 +01:00
|
|
|
for_each_reflog_ent(refname, read_one_reflog, reflogs);
|
|
|
|
if (reflogs->nr == 0) {
|
2015-09-24 23:07:03 +02:00
|
|
|
free(refname);
|
|
|
|
refname = xstrfmt("refs/heads/%s", ref);
|
2007-01-11 11:47:48 +01:00
|
|
|
for_each_reflog_ent(refname, read_one_reflog, reflogs);
|
|
|
|
}
|
|
|
|
free(refname);
|
|
|
|
}
|
|
|
|
return reflogs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_reflog_recno_by_time(struct complete_reflogs *array,
|
2017-04-26 21:29:31 +02:00
|
|
|
timestamp_t timestamp)
|
2007-01-11 11:47:48 +01:00
|
|
|
{
|
|
|
|
int i;
|
2007-01-20 10:49:15 +01:00
|
|
|
for (i = array->nr - 1; i >= 0; i--)
|
2007-01-11 11:47:48 +01:00
|
|
|
if (timestamp >= array->items[i].timestamp)
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct commit_info_lifo {
|
|
|
|
struct commit_info {
|
|
|
|
struct commit *commit;
|
|
|
|
void *util;
|
|
|
|
} *items;
|
|
|
|
int nr, alloc;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct commit_info *get_commit_info(struct commit *commit,
|
|
|
|
struct commit_info_lifo *lifo, int pop)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < lifo->nr; i++)
|
|
|
|
if (lifo->items[i].commit == commit) {
|
|
|
|
struct commit_info *result = &lifo->items[i];
|
|
|
|
if (pop) {
|
|
|
|
if (i + 1 < lifo->nr)
|
2017-07-15 22:00:45 +02:00
|
|
|
MOVE_ARRAY(lifo->items + i,
|
|
|
|
lifo->items + i + 1,
|
|
|
|
lifo->nr - i);
|
2007-01-11 11:47:48 +01:00
|
|
|
lifo->nr--;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_commit_info(struct commit *commit, void *util,
|
|
|
|
struct commit_info_lifo *lifo)
|
|
|
|
{
|
|
|
|
struct commit_info *info;
|
2014-03-03 23:31:57 +01:00
|
|
|
ALLOC_GROW(lifo->items, lifo->nr + 1, lifo->alloc);
|
2007-01-11 11:47:48 +01:00
|
|
|
info = lifo->items + lifo->nr;
|
|
|
|
info->commit = commit;
|
|
|
|
info->util = util;
|
|
|
|
lifo->nr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct commit_reflog {
|
2012-05-04 07:26:26 +02:00
|
|
|
int recno;
|
|
|
|
enum selector_type {
|
|
|
|
SELECTOR_NONE,
|
|
|
|
SELECTOR_INDEX,
|
|
|
|
SELECTOR_DATE
|
|
|
|
} selector;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct complete_reflogs *reflogs;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct reflog_walk_info {
|
|
|
|
struct commit_info_lifo reflogs;
|
2008-07-21 20:03:49 +02:00
|
|
|
struct string_list complete_reflogs;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct commit_reflog *last_commit_reflog;
|
|
|
|
};
|
|
|
|
|
2014-08-31 22:11:31 +02:00
|
|
|
void init_reflog_walk(struct reflog_walk_info **info)
|
2007-01-11 11:47:48 +01:00
|
|
|
{
|
2014-05-26 17:33:54 +02:00
|
|
|
*info = xcalloc(1, sizeof(struct reflog_walk_info));
|
reflog-walk: duplicate strings in complete_reflogs list
As part of the add_reflog_to_walk() function, we keep a
string_list mapping refnames to their reflog contents. This
serves as a cache so that accessing the same reflog twice
requires only a single copy of the log in memory.
The string_list is initialized via xcalloc, meaning its
strdup_strings field is set to 0. But after inserting a
string into the list, we unconditionally call free() on the
string, leaving the list pointing to freed memory. If
another reflog is added (e.g., "git log -g HEAD HEAD"), then
the second one may have unpredictable results.
The extra free was added by 5026b47175 (add_reflog_for_walk:
avoid memory leak, 2017-05-04). Though if you look
carefully, you can see that the code was buggy even before
then. If we tried to read the reflogs by time but came up
with no entries, we exited with an error, freeing the string
in that code path. So the bug was harder to trigger, but
still there.
We can fix it by just asking the string list to make a copy
of the string. Technically we could fix the problem by not
calling free() on our string (and just handing over
ownership to the string list), but there are enough
conditionals that it's quite hard to figure out which code
paths need the free and which do not. Simpler is better
here.
The new test reliably shows the problem when run with
--valgrind or ASAN.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-07 10:39:50 +02:00
|
|
|
(*info)->complete_reflogs.strdup_strings = 1;
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
|
|
|
|
2007-07-24 01:39:50 +02:00
|
|
|
int add_reflog_for_walk(struct reflog_walk_info *info,
|
2007-01-11 11:47:48 +01:00
|
|
|
struct commit *commit, const char *name)
|
|
|
|
{
|
2017-04-26 21:29:31 +02:00
|
|
|
timestamp_t timestamp = 0;
|
2007-01-11 11:47:48 +01:00
|
|
|
int recno = -1;
|
2008-07-21 20:03:49 +02:00
|
|
|
struct string_list_item *item;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct complete_reflogs *reflogs;
|
|
|
|
char *branch, *at = strchr(name, '@');
|
|
|
|
struct commit_reflog *commit_reflog;
|
2012-05-04 07:26:26 +02:00
|
|
|
enum selector_type selector = SELECTOR_NONE;
|
2007-01-11 11:47:48 +01:00
|
|
|
|
2007-01-20 03:28:19 +01:00
|
|
|
if (commit->object.flags & UNINTERESTING)
|
|
|
|
die ("Cannot walk reflogs for %s", name);
|
|
|
|
|
2007-01-11 11:47:48 +01:00
|
|
|
branch = xstrdup(name);
|
|
|
|
if (at && at[1] == '{') {
|
|
|
|
char *ep;
|
|
|
|
branch[at - name] = '\0';
|
|
|
|
recno = strtoul(at + 2, &ep, 10);
|
|
|
|
if (*ep != '}') {
|
|
|
|
recno = -1;
|
|
|
|
timestamp = approxidate(at + 2);
|
2012-05-04 07:26:26 +02:00
|
|
|
selector = SELECTOR_DATE;
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
2012-05-04 07:26:26 +02:00
|
|
|
else
|
|
|
|
selector = SELECTOR_INDEX;
|
2007-01-11 11:47:48 +01:00
|
|
|
} else
|
|
|
|
recno = 0;
|
|
|
|
|
2010-06-26 01:41:37 +02:00
|
|
|
item = string_list_lookup(&info->complete_reflogs, branch);
|
2007-01-11 11:47:48 +01:00
|
|
|
if (item)
|
|
|
|
reflogs = item->util;
|
|
|
|
else {
|
2007-02-02 00:07:24 +01:00
|
|
|
if (*branch == '\0') {
|
2017-02-22 00:47:29 +01:00
|
|
|
struct object_id oid;
|
2007-02-02 00:07:24 +01:00
|
|
|
free(branch);
|
2017-02-22 00:47:29 +01:00
|
|
|
branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
|
2011-12-13 15:17:48 +01:00
|
|
|
if (!branch)
|
|
|
|
die ("No current branch");
|
|
|
|
|
2007-02-02 00:07:24 +01:00
|
|
|
}
|
2007-01-11 11:47:48 +01:00
|
|
|
reflogs = read_complete_reflog(branch);
|
2007-02-09 01:28:23 +01:00
|
|
|
if (!reflogs || reflogs->nr == 0) {
|
2017-02-22 00:47:29 +01:00
|
|
|
struct object_id oid;
|
2007-02-09 01:28:23 +01:00
|
|
|
char *b;
|
2017-05-04 15:58:42 +02:00
|
|
|
int ret = dwim_log(branch, strlen(branch),
|
|
|
|
oid.hash, &b);
|
|
|
|
if (ret > 1)
|
|
|
|
free(b);
|
|
|
|
else if (ret == 1) {
|
2017-07-07 10:43:16 +02:00
|
|
|
free_complete_reflog(reflogs);
|
2007-02-09 01:28:23 +01:00
|
|
|
free(branch);
|
|
|
|
branch = b;
|
|
|
|
reflogs = read_complete_reflog(branch);
|
|
|
|
}
|
|
|
|
}
|
2017-05-04 15:58:42 +02:00
|
|
|
if (!reflogs || reflogs->nr == 0) {
|
2017-07-07 10:43:16 +02:00
|
|
|
free_complete_reflog(reflogs);
|
2017-05-04 15:58:42 +02:00
|
|
|
free(branch);
|
2007-07-24 01:39:50 +02:00
|
|
|
return -1;
|
2017-05-04 15:58:42 +02:00
|
|
|
}
|
2010-06-26 01:41:35 +02:00
|
|
|
string_list_insert(&info->complete_reflogs, branch)->util
|
2007-01-11 11:47:48 +01:00
|
|
|
= reflogs;
|
|
|
|
}
|
2017-05-04 15:58:42 +02:00
|
|
|
free(branch);
|
2007-01-11 11:47:48 +01:00
|
|
|
|
2014-05-26 17:33:54 +02:00
|
|
|
commit_reflog = xcalloc(1, sizeof(struct commit_reflog));
|
2007-01-11 11:47:48 +01:00
|
|
|
if (recno < 0) {
|
|
|
|
commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
|
|
|
|
if (commit_reflog->recno < 0) {
|
|
|
|
free(commit_reflog);
|
2007-07-24 01:39:50 +02:00
|
|
|
return -1;
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
commit_reflog->recno = reflogs->nr - recno - 1;
|
2012-05-04 07:26:26 +02:00
|
|
|
commit_reflog->selector = selector;
|
2007-01-11 11:47:48 +01:00
|
|
|
commit_reflog->reflogs = reflogs;
|
|
|
|
|
|
|
|
add_commit_info(commit, commit_reflog, &info->reflogs);
|
2007-07-24 01:39:50 +02:00
|
|
|
return 0;
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
|
|
|
|
{
|
|
|
|
struct commit_info *commit_info =
|
|
|
|
get_commit_info(commit, &info->reflogs, 0);
|
|
|
|
struct commit_reflog *commit_reflog;
|
2016-01-05 22:12:10 +01:00
|
|
|
struct object *logobj;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct reflog_info *reflog;
|
|
|
|
|
|
|
|
info->last_commit_reflog = NULL;
|
|
|
|
if (!commit_info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
commit_reflog = commit_info->util;
|
|
|
|
if (commit_reflog->recno < 0) {
|
|
|
|
commit->parents = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
info->last_commit_reflog = commit_reflog;
|
2016-01-05 22:12:10 +01:00
|
|
|
|
|
|
|
do {
|
|
|
|
reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
|
|
|
|
commit_reflog->recno--;
|
object: convert parse_object* to take struct object_id
Make parse_object, parse_object_or_die, and parse_object_buffer take a
pointer to struct object_id. Remove the temporary variables inserted
earlier, since they are no longer necessary. Transform all of the
callers using the following semantic patch:
@@
expression E1;
@@
- parse_object(E1.hash)
+ parse_object(&E1)
@@
expression E1;
@@
- parse_object(E1->hash)
+ parse_object(E1)
@@
expression E1, E2;
@@
- parse_object_or_die(E1.hash, E2)
+ parse_object_or_die(&E1, E2)
@@
expression E1, E2;
@@
- parse_object_or_die(E1->hash, E2)
+ parse_object_or_die(E1, E2)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1.hash, E2, E3, E4, E5)
+ parse_object_buffer(&E1, E2, E3, E4, E5)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1->hash, E2, E3, E4, E5)
+ parse_object_buffer(E1, E2, E3, E4, E5)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 00:10:38 +02:00
|
|
|
logobj = parse_object(&reflog->ooid);
|
2016-01-05 22:12:10 +01:00
|
|
|
} while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
|
|
|
|
|
2017-05-01 04:28:55 +02:00
|
|
|
if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) {
|
2016-06-03 22:42:35 +02:00
|
|
|
/* a root commit, but there are still more entries to show */
|
|
|
|
reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
|
object: convert parse_object* to take struct object_id
Make parse_object, parse_object_or_die, and parse_object_buffer take a
pointer to struct object_id. Remove the temporary variables inserted
earlier, since they are no longer necessary. Transform all of the
callers using the following semantic patch:
@@
expression E1;
@@
- parse_object(E1.hash)
+ parse_object(&E1)
@@
expression E1;
@@
- parse_object(E1->hash)
+ parse_object(E1)
@@
expression E1, E2;
@@
- parse_object_or_die(E1.hash, E2)
+ parse_object_or_die(&E1, E2)
@@
expression E1, E2;
@@
- parse_object_or_die(E1->hash, E2)
+ parse_object_or_die(E1, E2)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1.hash, E2, E3, E4, E5)
+ parse_object_buffer(&E1, E2, E3, E4, E5)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1->hash, E2, E3, E4, E5)
+ parse_object_buffer(E1, E2, E3, E4, E5)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 00:10:38 +02:00
|
|
|
logobj = parse_object(&reflog->noid);
|
reflog-walk: skip over double-null oid due to HEAD rename
Since 39ee4c6c2f (branch: record creation of renamed branch
in HEAD's log, 2017-02-20), a rename on the currently
checked out branch will create two entries in the HEAD
reflog: one where the branch goes away (switching to the
null oid), and one where it comes back (switching away from
the null oid).
This confuses the reflog-walk code. When walking backwards,
it first sees the null oid in the "old" field of the second
entry. Thanks to the "root commit" logic added by 71abeb753f
(reflog: continue walking the reflog past root commits,
2016-06-03), we keep looking for the next entry by scanning
the "new" field from the previous entry. But that field is
also null! We need to go just a tiny bit further, and look
at its "old" field. But with the current code, we decide the
reflog has nothing else to show and just give up. To the
user this looks like the reflog was truncated by the rename
operation, when in fact those entries are still there.
This patch does the absolute minimal fix, which is to look
back that one extra level and keep traversing.
The resulting behavior may not be the _best_ thing to do in
the long run (for example, we show both reflog entries each
with the same commit id), but it's a simple way to fix the
problem without risking further regressions.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-05 09:57:37 +02:00
|
|
|
if (!logobj)
|
2017-07-10 22:42:52 +02:00
|
|
|
logobj = parse_object(&reflog->ooid);
|
2016-06-03 22:42:35 +02:00
|
|
|
}
|
|
|
|
|
2016-01-05 22:12:10 +01:00
|
|
|
if (!logobj || logobj->type != OBJ_COMMIT) {
|
|
|
|
commit_info->commit = NULL;
|
2007-01-11 11:47:48 +01:00
|
|
|
commit->parents = NULL;
|
|
|
|
return;
|
|
|
|
}
|
2016-01-05 22:12:10 +01:00
|
|
|
commit_info->commit = (struct commit *)logobj;
|
2007-01-11 11:47:48 +01:00
|
|
|
|
2014-05-26 17:33:54 +02:00
|
|
|
commit->parents = xcalloc(1, sizeof(struct commit_list));
|
2007-01-11 11:47:48 +01:00
|
|
|
commit->parents->item = commit_info->commit;
|
|
|
|
}
|
|
|
|
|
2009-10-19 17:48:09 +02:00
|
|
|
void get_reflog_selector(struct strbuf *sb,
|
|
|
|
struct reflog_walk_info *reflog_info,
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 18:55:02 +02:00
|
|
|
const struct date_mode *dmode, int force_date,
|
2009-10-19 17:48:10 +02:00
|
|
|
int shorten)
|
2009-10-19 17:48:09 +02:00
|
|
|
{
|
|
|
|
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
|
|
|
|
struct reflog_info *info;
|
2009-10-19 17:48:10 +02:00
|
|
|
const char *printed_ref;
|
2009-10-19 17:48:09 +02:00
|
|
|
|
|
|
|
if (!commit_reflog)
|
|
|
|
return;
|
|
|
|
|
2009-10-19 17:48:10 +02:00
|
|
|
if (shorten) {
|
|
|
|
if (!commit_reflog->reflogs->short_ref)
|
|
|
|
commit_reflog->reflogs->short_ref
|
|
|
|
= shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0);
|
|
|
|
printed_ref = commit_reflog->reflogs->short_ref;
|
|
|
|
} else {
|
|
|
|
printed_ref = commit_reflog->reflogs->ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(sb, "%s@{", printed_ref);
|
reflog-walk: always make HEAD@{0} show indexed selectors
When we are showing reflog selectors during a walk, we infer
from context whether the user wanted to see the index in
each selector, or the reflog date. The current rules are:
1. if the user asked for an explicit date format in the
output, show the date
2. if the user asked for ref@{now}, show the date
3. if neither is true, show the index
However, if we see "ref@{0}", that should be a strong clue
that the user wants to see the counted version. In fact, it
should be much stronger than the date format in (1). The
user may have been setting the date format to use in another
part of the output (e.g., in --format="%gd (%ad)", they may
have wanted to influence the author date).
This patch flips the rules to:
1. if the user asked for ref@{0}, always show the index
2. if the user asked for ref@{now}, always show the date
3. otherwise, we have just "ref"; show them counted by
default, but respect the presence of "--date" as a clue
that the user wanted them date-based
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-05-04 07:27:25 +02:00
|
|
|
if (commit_reflog->selector == SELECTOR_DATE ||
|
2012-05-07 23:11:32 +02:00
|
|
|
(commit_reflog->selector == SELECTOR_NONE && force_date)) {
|
2009-10-19 17:48:09 +02:00
|
|
|
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
|
|
|
|
strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode));
|
|
|
|
} else {
|
|
|
|
strbuf_addf(sb, "%d", commit_reflog->reflogs->nr
|
|
|
|
- 2 - commit_reflog->recno);
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addch(sb, '}');
|
|
|
|
}
|
|
|
|
|
2009-10-19 17:48:10 +02:00
|
|
|
void get_reflog_message(struct strbuf *sb,
|
|
|
|
struct reflog_walk_info *reflog_info)
|
|
|
|
{
|
|
|
|
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
|
|
|
|
struct reflog_info *info;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (!commit_reflog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
|
|
|
|
len = strlen(info->message);
|
|
|
|
if (len > 0)
|
|
|
|
len--; /* strip away trailing newline */
|
|
|
|
strbuf_add(sb, info->message, len);
|
|
|
|
}
|
|
|
|
|
2011-12-16 12:40:24 +01:00
|
|
|
const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
|
|
|
|
{
|
|
|
|
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
|
|
|
|
struct reflog_info *info;
|
|
|
|
|
|
|
|
if (!commit_reflog)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
|
|
|
|
return info->email;
|
|
|
|
}
|
|
|
|
|
2009-10-19 17:48:09 +02:00
|
|
|
void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 18:55:02 +02:00
|
|
|
const struct date_mode *dmode, int force_date)
|
2007-01-11 11:47:48 +01:00
|
|
|
{
|
2009-10-19 17:48:09 +02:00
|
|
|
if (reflog_info && reflog_info->last_commit_reflog) {
|
|
|
|
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
|
2007-01-11 11:47:48 +01:00
|
|
|
struct reflog_info *info;
|
2009-10-19 17:48:09 +02:00
|
|
|
struct strbuf selector = STRBUF_INIT;
|
2007-01-11 11:47:48 +01:00
|
|
|
|
2007-01-20 09:51:41 +01:00
|
|
|
info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
|
2012-05-07 23:11:32 +02:00
|
|
|
get_reflog_selector(&selector, reflog_info, dmode, force_date, 0);
|
2007-01-20 09:51:41 +01:00
|
|
|
if (oneline) {
|
2009-10-19 17:48:09 +02:00
|
|
|
printf("%s: %s", selector.buf, info->message);
|
2007-01-20 09:51:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2009-10-19 17:48:09 +02:00
|
|
|
printf("Reflog: %s (%s)\nReflog message: %s",
|
|
|
|
selector.buf, info->email, info->message);
|
2007-01-20 09:51:41 +01:00
|
|
|
}
|
2009-10-19 17:48:09 +02:00
|
|
|
|
|
|
|
strbuf_release(&selector);
|
2007-01-11 11:47:48 +01:00
|
|
|
}
|
|
|
|
}
|