1 #include "git-compat-util.h"
5 #include "repository.h"
7 #include "string-list.h"
8 #include "reflog-walk.h"
10 struct complete_reflogs
{
14 struct object_id ooid
, noid
;
16 timestamp_t timestamp
;
23 static int read_one_reflog(struct object_id
*ooid
, struct object_id
*noid
,
24 const char *email
, timestamp_t timestamp
, int tz
,
25 const char *message
, void *cb_data
)
27 struct complete_reflogs
*array
= cb_data
;
28 struct reflog_info
*item
;
30 ALLOC_GROW(array
->items
, array
->nr
+ 1, array
->alloc
);
31 item
= array
->items
+ array
->nr
;
32 oidcpy(&item
->ooid
, ooid
);
33 oidcpy(&item
->noid
, noid
);
34 item
->email
= xstrdup(email
);
35 item
->timestamp
= timestamp
;
37 item
->message
= xstrdup(message
);
42 static void free_complete_reflog(struct complete_reflogs
*array
)
49 for (i
= 0; i
< array
->nr
; i
++) {
50 free(array
->items
[i
].email
);
51 free(array
->items
[i
].message
);
55 free(array
->short_ref
);
59 static void complete_reflogs_clear(void *util
, const char *str UNUSED
)
61 struct complete_reflogs
*array
= util
;
62 free_complete_reflog(array
);
65 static struct complete_reflogs
*read_complete_reflog(const char *ref
)
67 struct complete_reflogs
*reflogs
=
68 xcalloc(1, sizeof(struct complete_reflogs
));
69 reflogs
->ref
= xstrdup(ref
);
70 refs_for_each_reflog_ent(get_main_ref_store(the_repository
), ref
,
71 read_one_reflog
, reflogs
);
72 if (reflogs
->nr
== 0) {
75 name
= name_to_free
= refs_resolve_refdup(get_main_ref_store(the_repository
),
80 refs_for_each_reflog_ent(get_main_ref_store(the_repository
),
81 name
, read_one_reflog
,
86 if (reflogs
->nr
== 0) {
87 char *refname
= xstrfmt("refs/%s", ref
);
88 refs_for_each_reflog_ent(get_main_ref_store(the_repository
),
89 refname
, read_one_reflog
, reflogs
);
90 if (reflogs
->nr
== 0) {
92 refname
= xstrfmt("refs/heads/%s", ref
);
93 refs_for_each_reflog_ent(get_main_ref_store(the_repository
),
94 refname
, read_one_reflog
,
102 static int get_reflog_recno_by_time(struct complete_reflogs
*array
,
103 timestamp_t timestamp
)
106 for (i
= array
->nr
- 1; i
>= 0; i
--)
107 if (timestamp
>= array
->items
[i
].timestamp
)
112 struct commit_reflog
{
119 struct complete_reflogs
*reflogs
;
122 struct reflog_walk_info
{
123 struct commit_reflog
**logs
;
125 struct string_list complete_reflogs
;
126 struct commit_reflog
*last_commit_reflog
;
129 void init_reflog_walk(struct reflog_walk_info
**info
)
131 CALLOC_ARRAY(*info
, 1);
132 (*info
)->complete_reflogs
.strdup_strings
= 1;
135 void reflog_walk_info_release(struct reflog_walk_info
*info
)
142 for (i
= 0; i
< info
->nr
; i
++)
144 string_list_clear_func(&info
->complete_reflogs
,
145 complete_reflogs_clear
);
150 int add_reflog_for_walk(struct reflog_walk_info
*info
,
151 struct commit
*commit
, const char *name
)
153 timestamp_t timestamp
= 0;
155 struct string_list_item
*item
;
156 struct complete_reflogs
*reflogs
;
157 char *branch
, *at
= strchr(name
, '@');
158 struct commit_reflog
*commit_reflog
;
159 enum selector_type selector
= SELECTOR_NONE
;
161 if (commit
->object
.flags
& UNINTERESTING
)
162 die("cannot walk reflogs for %s", name
);
164 branch
= xstrdup(name
);
165 if (at
&& at
[1] == '{') {
167 branch
[at
- name
] = '\0';
168 recno
= strtoul(at
+ 2, &ep
, 10);
171 timestamp
= approxidate(at
+ 2);
172 selector
= SELECTOR_DATE
;
175 selector
= SELECTOR_INDEX
;
179 item
= string_list_lookup(&info
->complete_reflogs
, branch
);
181 reflogs
= item
->util
;
183 if (*branch
== '\0') {
185 branch
= refs_resolve_refdup(get_main_ref_store(the_repository
),
186 "HEAD", 0, NULL
, NULL
);
188 die("no current branch");
191 reflogs
= read_complete_reflog(branch
);
192 if (!reflogs
|| reflogs
->nr
== 0) {
194 int ret
= dwim_log(branch
, strlen(branch
),
199 free_complete_reflog(reflogs
);
202 reflogs
= read_complete_reflog(branch
);
205 if (!reflogs
|| reflogs
->nr
== 0) {
206 free_complete_reflog(reflogs
);
210 string_list_insert(&info
->complete_reflogs
, branch
)->util
215 CALLOC_ARRAY(commit_reflog
, 1);
217 commit_reflog
->recno
= get_reflog_recno_by_time(reflogs
, timestamp
);
218 if (commit_reflog
->recno
< 0) {
223 commit_reflog
->recno
= reflogs
->nr
- recno
- 1;
224 commit_reflog
->selector
= selector
;
225 commit_reflog
->reflogs
= reflogs
;
227 ALLOC_GROW(info
->logs
, info
->nr
+ 1, info
->alloc
);
228 info
->logs
[info
->nr
++] = commit_reflog
;
233 void get_reflog_selector(struct strbuf
*sb
,
234 struct reflog_walk_info
*reflog_info
,
235 struct date_mode dmode
, int force_date
,
238 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
239 struct reflog_info
*info
;
240 const char *printed_ref
;
246 if (!commit_reflog
->reflogs
->short_ref
)
247 commit_reflog
->reflogs
->short_ref
248 = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository
),
249 commit_reflog
->reflogs
->ref
,
251 printed_ref
= commit_reflog
->reflogs
->short_ref
;
253 printed_ref
= commit_reflog
->reflogs
->ref
;
256 strbuf_addf(sb
, "%s@{", printed_ref
);
257 if (commit_reflog
->selector
== SELECTOR_DATE
||
258 (commit_reflog
->selector
== SELECTOR_NONE
&& force_date
)) {
259 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
260 strbuf_addstr(sb
, show_date(info
->timestamp
, info
->tz
, dmode
));
262 strbuf_addf(sb
, "%d", commit_reflog
->reflogs
->nr
263 - 2 - commit_reflog
->recno
);
266 strbuf_addch(sb
, '}');
269 void get_reflog_message(struct strbuf
*sb
,
270 struct reflog_walk_info
*reflog_info
)
272 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
273 struct reflog_info
*info
;
279 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
280 len
= strlen(info
->message
);
282 len
--; /* strip away trailing newline */
283 strbuf_add(sb
, info
->message
, len
);
286 const char *get_reflog_ident(struct reflog_walk_info
*reflog_info
)
288 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
289 struct reflog_info
*info
;
294 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
298 timestamp_t
get_reflog_timestamp(struct reflog_walk_info
*reflog_info
)
300 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
301 struct reflog_info
*info
;
306 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
307 return info
->timestamp
;
310 void show_reflog_message(struct reflog_walk_info
*reflog_info
, int oneline
,
311 struct date_mode dmode
, int force_date
)
313 if (reflog_info
&& reflog_info
->last_commit_reflog
) {
314 struct commit_reflog
*commit_reflog
= reflog_info
->last_commit_reflog
;
315 struct reflog_info
*info
;
316 struct strbuf selector
= STRBUF_INIT
;
318 info
= &commit_reflog
->reflogs
->items
[commit_reflog
->recno
+1];
319 get_reflog_selector(&selector
, reflog_info
, dmode
, force_date
, 0);
321 printf("%s: %s", selector
.buf
, info
->message
);
324 printf("Reflog: %s (%s)\nReflog message: %s",
325 selector
.buf
, info
->email
, info
->message
);
328 strbuf_release(&selector
);
332 int reflog_walk_empty(struct reflog_walk_info
*info
)
334 return !info
|| !info
->nr
;
337 static struct commit
*next_reflog_commit(struct commit_reflog
*log
)
339 for (; log
->recno
>= 0; log
->recno
--) {
340 struct reflog_info
*entry
= &log
->reflogs
->items
[log
->recno
];
341 struct object
*obj
= parse_object(the_repository
,
344 if (obj
&& obj
->type
== OBJ_COMMIT
)
345 return (struct commit
*)obj
;
350 static timestamp_t
log_timestamp(struct commit_reflog
*log
)
352 return log
->reflogs
->items
[log
->recno
].timestamp
;
355 struct commit
*next_reflog_entry(struct reflog_walk_info
*walk
)
357 struct commit_reflog
*best
= NULL
;
358 struct commit
*best_commit
= NULL
;
361 for (i
= 0; i
< walk
->nr
; i
++) {
362 struct commit_reflog
*log
= walk
->logs
[i
];
363 struct commit
*commit
= next_reflog_commit(log
);
368 if (!best
|| log_timestamp(log
) > log_timestamp(best
)) {
370 best_commit
= commit
;
376 walk
->last_commit_reflog
= best
;