6 #include "string-list.h"
7 #include "reflog-walk.h"
9 struct complete_reflogs
{
11 const char *short_ref
;
13 struct object_id ooid
, noid
;
15 unsigned long timestamp
;
22 static int read_one_reflog(struct object_id
*ooid
, struct object_id
*noid
,
23 const char *email
, unsigned long timestamp
, int tz
,
24 const char *message
, void *cb_data
)
26 struct complete_reflogs
*array
= cb_data
;
27 struct reflog_info
*item
;
29 ALLOC_GROW(array
->items
, array
->nr
+ 1, array
->alloc
);
30 item
= array
->items
+ array
->nr
;
31 oidcpy(&item
->ooid
, ooid
);
32 oidcpy(&item
->noid
, noid
);
33 item
->email
= xstrdup(email
);
34 item
->timestamp
= timestamp
;
36 item
->message
= xstrdup(message
);
41 static void free_complete_reflog(struct complete_reflogs
*array
)
48 for (i
= 0; i
< array
->nr
; i
++) {
49 free(array
->items
[i
].email
);
50 free(array
->items
[i
].message
);
57 static struct complete_reflogs
*read_complete_reflog(const char *ref
)
59 struct complete_reflogs
*reflogs
=
60 xcalloc(1, sizeof(struct complete_reflogs
));
61 reflogs
->ref
= xstrdup(ref
);
62 for_each_reflog_ent(ref
, read_one_reflog
, reflogs
);
63 if (reflogs
->nr
== 0) {
67 name
= name_to_free
= resolve_refdup(ref
, RESOLVE_REF_READING
,
70 for_each_reflog_ent(name
, read_one_reflog
, reflogs
);
74 if (reflogs
->nr
== 0) {
75 char *refname
= xstrfmt("refs/%s", ref
);
76 for_each_reflog_ent(refname
, read_one_reflog
, reflogs
);
77 if (reflogs
->nr
== 0) {
79 refname
= xstrfmt("refs/heads/%s", ref
);
80 for_each_reflog_ent(refname
, read_one_reflog
, reflogs
);
87 static int get_reflog_recno_by_time(struct complete_reflogs
*array
,
88 unsigned long timestamp
)
91 for (i
= array
->nr
- 1; i
>= 0; i
--)
92 if (timestamp
>= array
->items
[i
].timestamp
)
97 struct commit_info_lifo
{
99 struct commit
*commit
;
105 static struct commit_info
*get_commit_info(struct commit
*commit
,
106 struct commit_info_lifo
*lifo
, int pop
)
109 for (i
= 0; i
< lifo
->nr
; i
++)
110 if (lifo
->items
[i
].commit
== commit
) {
111 struct commit_info
*result
= &lifo
->items
[i
];
113 if (i
+ 1 < lifo
->nr
)
114 memmove(lifo
->items
+ i
,
117 sizeof(struct commit_info
));
125 static void add_commit_info(struct commit
*commit
, void *util
,
126 struct commit_info_lifo
*lifo
)
128 struct commit_info
*info
;
129 ALLOC_GROW(lifo
->items
, lifo
->nr
+ 1, lifo
->alloc
);
130 info
= lifo
->items
+ lifo
->nr
;
131 info
->commit
= commit
;
136 struct commit_reflog
{
143 struct complete_reflogs
*reflogs
;
146 struct reflog_walk_info
{
147 struct commit_info_lifo reflogs
;
148 struct string_list complete_reflogs
;
149 struct commit_reflog
*last_commit_reflog
;
152 void init_reflog_walk(struct reflog_walk_info
**info
)
154 *info
= xcalloc(1, sizeof(struct reflog_walk_info
));
155 (*info
)->complete_reflogs
.strdup_strings
= 1;
158 int add_reflog_for_walk(struct reflog_walk_info
*info
,
159 struct commit
*commit
, const char *name
)
161 unsigned long timestamp
= 0;
163 struct string_list_item
*item
;
164 struct complete_reflogs
*reflogs
;
165 char *branch
, *at
= strchr(name
, '@');
166 struct commit_reflog
*commit_reflog
;
167 enum selector_type selector
= SELECTOR_NONE
;
169 if (commit
->object
.flags
& UNINTERESTING
)
170 die ("Cannot walk reflogs for %s", name
);
172 branch
= xstrdup(name
);
173 if (at
&& at
[1] == '{') {
175 branch
[at
- name
] = '\0';
176 recno
= strtoul(at
+ 2, &ep
, 10);
179 timestamp
= approxidate(at
+ 2);
180 selector
= SELECTOR_DATE
;
183 selector
= SELECTOR_INDEX
;
187 item
= string_list_lookup(&info
->complete_reflogs
, branch
);
189 reflogs
= item
->util
;
191 if (*branch
== '\0') {
192 struct object_id oid
;
194 branch
= resolve_refdup("HEAD", 0, oid
.hash
, NULL
);
196 die ("No current branch");
199 reflogs
= read_complete_reflog(branch
);
200 if (!reflogs
|| reflogs
->nr
== 0) {
201 struct object_id oid
;
203 int ret
= dwim_log(branch
, strlen(branch
),
208 free_complete_reflog(reflogs
);
211 reflogs
= read_complete_reflog(branch
);
214 if (!reflogs
|| reflogs
->nr
== 0) {
215 free_complete_reflog(reflogs
);
219 string_list_insert(&info
->complete_reflogs
, branch
)->util
224 commit_reflog
= xcalloc(1, sizeof(struct commit_reflog
));
226 commit_reflog
->recno
= get_reflog_recno_by_time(reflogs
, timestamp
);
227 if (commit_reflog
->recno
< 0) {
232 commit_reflog
->recno
= reflogs
->nr
- recno
- 1;
233 commit_reflog
->selector
= selector
;
234 commit_reflog
->reflogs
= reflogs
;
236 add_commit_info(commit
, commit_reflog
, &info
->reflogs
);
240 void fake_reflog_parent(struct reflog_walk_info
*info
, struct commit
*commit
)
242 struct commit_info
*commit_info
=
243 get_commit_info(commit
, &info
->reflogs
, 0);
244 struct commit_reflog
*commit_reflog
;
245 struct object
*logobj
;
246 struct reflog_info
*reflog
;
248 info
->last_commit_reflog
= NULL
;
252 commit_reflog
= commit_info
->util
;
253 if (commit_reflog
->recno
< 0) {
254 commit
->parents
= NULL
;
257 info
->last_commit_reflog
= commit_reflog
;
260 reflog
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
];
261 commit_reflog
->recno
--;
262 logobj
= parse_object(reflog
->ooid
.hash
);
263 } while (commit_reflog
->recno
&& (logobj
&& logobj
->type
!= OBJ_COMMIT
));
265 if (!logobj
&& commit_reflog
->recno
>= 0 && is_null_sha1(reflog
->ooid
.hash
)) {
266 /* a root commit, but there are still more entries to show */
267 reflog
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
];
268 logobj
= parse_object(reflog
->noid
.hash
);
270 logobj
= parse_object(reflog
->ooid
.hash
);
273 if (!logobj
|| logobj
->type
!= OBJ_COMMIT
) {
274 commit_info
->commit
= NULL
;
275 commit
->parents
= NULL
;
278 commit_info
->commit
= (struct commit
*)logobj
;
280 commit
->parents
= xcalloc(1, sizeof(struct commit_list
));
281 commit
->parents
->item
= commit_info
->commit
;
284 void get_reflog_selector(struct strbuf
*sb
,
285 struct reflog_walk_info
*reflog_info
,
286 const struct date_mode
*dmode
, int force_date
,
289 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
290 struct reflog_info
*info
;
291 const char *printed_ref
;
297 if (!commit_reflog
->reflogs
->short_ref
)
298 commit_reflog
->reflogs
->short_ref
299 = shorten_unambiguous_ref(commit_reflog
->reflogs
->ref
, 0);
300 printed_ref
= commit_reflog
->reflogs
->short_ref
;
302 printed_ref
= commit_reflog
->reflogs
->ref
;
305 strbuf_addf(sb
, "%s@{", printed_ref
);
306 if (commit_reflog
->selector
== SELECTOR_DATE
||
307 (commit_reflog
->selector
== SELECTOR_NONE
&& force_date
)) {
308 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
309 strbuf_addstr(sb
, show_date(info
->timestamp
, info
->tz
, dmode
));
311 strbuf_addf(sb
, "%d", commit_reflog
->reflogs
->nr
312 - 2 - commit_reflog
->recno
);
315 strbuf_addch(sb
, '}');
318 void get_reflog_message(struct strbuf
*sb
,
319 struct reflog_walk_info
*reflog_info
)
321 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
322 struct reflog_info
*info
;
328 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
329 len
= strlen(info
->message
);
331 len
--; /* strip away trailing newline */
332 strbuf_add(sb
, info
->message
, len
);
335 const char *get_reflog_ident(struct reflog_walk_info
*reflog_info
)
337 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
338 struct reflog_info
*info
;
343 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
347 void show_reflog_message(struct reflog_walk_info
*reflog_info
, int oneline
,
348 const struct date_mode
*dmode
, int force_date
)
350 if (reflog_info
&& reflog_info
->last_commit_reflog
) {
351 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
352 struct reflog_info
*info
;
353 struct strbuf selector
= STRBUF_INIT
;
355 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
356 get_reflog_selector(&selector
, reflog_info
, dmode
, force_date
, 0);
358 printf("%s: %s", selector
.buf
, info
->message
);
361 printf("Reflog: %s (%s)\nReflog message: %s",
362 selector
.buf
, info
->email
, info
->message
);
365 strbuf_release(&selector
);