Git fork
at reftables-rust 384 lines 9.2 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2 3#include "git-compat-util.h" 4#include "commit.h" 5#include "refs.h" 6#include "diff.h" 7#include "repository.h" 8#include "revision.h" 9#include "string-list.h" 10#include "reflog-walk.h" 11 12struct complete_reflogs { 13 char *ref; 14 char *short_ref; 15 struct reflog_info { 16 struct object_id ooid, noid; 17 char *email; 18 timestamp_t timestamp; 19 int tz; 20 char *message; 21 } *items; 22 int nr, alloc; 23}; 24 25static int read_one_reflog(const char *refname UNUSED, 26 struct object_id *ooid, struct object_id *noid, 27 const char *email, timestamp_t timestamp, int tz, 28 const char *message, void *cb_data) 29{ 30 struct complete_reflogs *array = cb_data; 31 struct reflog_info *item; 32 33 ALLOC_GROW(array->items, array->nr + 1, array->alloc); 34 item = array->items + array->nr; 35 oidcpy(&item->ooid, ooid); 36 oidcpy(&item->noid, noid); 37 item->email = xstrdup(email); 38 item->timestamp = timestamp; 39 item->tz = tz; 40 item->message = xstrdup(message); 41 array->nr++; 42 return 0; 43} 44 45static void free_complete_reflog(struct complete_reflogs *array) 46{ 47 int i; 48 49 if (!array) 50 return; 51 52 for (i = 0; i < array->nr; i++) { 53 free(array->items[i].email); 54 free(array->items[i].message); 55 } 56 free(array->items); 57 free(array->ref); 58 free(array->short_ref); 59 free(array); 60} 61 62static void complete_reflogs_clear(void *util, const char *str UNUSED) 63{ 64 struct complete_reflogs *array = util; 65 free_complete_reflog(array); 66} 67 68static struct complete_reflogs *read_complete_reflog(const char *ref) 69{ 70 struct complete_reflogs *reflogs = 71 xcalloc(1, sizeof(struct complete_reflogs)); 72 reflogs->ref = xstrdup(ref); 73 refs_for_each_reflog_ent(get_main_ref_store(the_repository), ref, 74 read_one_reflog, reflogs); 75 if (reflogs->nr == 0) { 76 const char *name; 77 void *name_to_free; 78 name = name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository), 79 ref, 80 RESOLVE_REF_READING, 81 NULL, NULL); 82 if (name) { 83 refs_for_each_reflog_ent(get_main_ref_store(the_repository), 84 name, read_one_reflog, 85 reflogs); 86 free(name_to_free); 87 } 88 } 89 if (reflogs->nr == 0) { 90 char *refname = xstrfmt("refs/%s", ref); 91 refs_for_each_reflog_ent(get_main_ref_store(the_repository), 92 refname, read_one_reflog, reflogs); 93 if (reflogs->nr == 0) { 94 free(refname); 95 refname = xstrfmt("refs/heads/%s", ref); 96 refs_for_each_reflog_ent(get_main_ref_store(the_repository), 97 refname, read_one_reflog, 98 reflogs); 99 } 100 free(refname); 101 } 102 return reflogs; 103} 104 105static int get_reflog_recno_by_time(struct complete_reflogs *array, 106 timestamp_t timestamp) 107{ 108 int i; 109 for (i = array->nr - 1; i >= 0; i--) 110 if (timestamp >= array->items[i].timestamp) 111 return i; 112 return -1; 113} 114 115struct commit_reflog { 116 int recno; 117 enum selector_type { 118 SELECTOR_NONE, 119 SELECTOR_INDEX, 120 SELECTOR_DATE 121 } selector; 122 struct complete_reflogs *reflogs; 123}; 124 125struct reflog_walk_info { 126 struct commit_reflog **logs; 127 size_t nr, alloc; 128 struct string_list complete_reflogs; 129 struct commit_reflog *last_commit_reflog; 130}; 131 132void init_reflog_walk(struct reflog_walk_info **info) 133{ 134 CALLOC_ARRAY(*info, 1); 135 (*info)->complete_reflogs.strdup_strings = 1; 136} 137 138void reflog_walk_info_release(struct reflog_walk_info *info) 139{ 140 size_t i; 141 142 if (!info) 143 return; 144 145 for (i = 0; i < info->nr; i++) 146 free(info->logs[i]); 147 string_list_clear_func(&info->complete_reflogs, 148 complete_reflogs_clear); 149 free(info->logs); 150 free(info); 151} 152 153int add_reflog_for_walk(struct reflog_walk_info *info, 154 struct commit *commit, const char *name) 155{ 156 timestamp_t timestamp = 0; 157 int recno = -1; 158 struct string_list_item *item; 159 struct complete_reflogs *reflogs; 160 char *branch, *at = strchr(name, '@'); 161 struct commit_reflog *commit_reflog; 162 enum selector_type selector = SELECTOR_NONE; 163 164 if (commit->object.flags & UNINTERESTING) 165 die("cannot walk reflogs for %s", name); 166 167 branch = xstrdup(name); 168 if (at && at[1] == '{') { 169 char *ep; 170 branch[at - name] = '\0'; 171 recno = strtoul(at + 2, &ep, 10); 172 if (*ep != '}') { 173 recno = -1; 174 timestamp = approxidate(at + 2); 175 selector = SELECTOR_DATE; 176 } 177 else 178 selector = SELECTOR_INDEX; 179 } else 180 recno = 0; 181 182 item = string_list_lookup(&info->complete_reflogs, branch); 183 if (item) 184 reflogs = item->util; 185 else { 186 if (*branch == '\0') { 187 free(branch); 188 branch = refs_resolve_refdup(get_main_ref_store(the_repository), 189 "HEAD", 0, NULL, NULL); 190 if (!branch) 191 die("no current branch"); 192 193 } 194 reflogs = read_complete_reflog(branch); 195 if (!reflogs || reflogs->nr == 0) { 196 char *b; 197 int ret = repo_dwim_log(the_repository, branch, strlen(branch), 198 NULL, &b); 199 if (ret > 1) 200 free(b); 201 else if (ret == 1) { 202 free_complete_reflog(reflogs); 203 free(branch); 204 branch = b; 205 reflogs = read_complete_reflog(branch); 206 } 207 } 208 if (!reflogs || reflogs->nr == 0) { 209 free_complete_reflog(reflogs); 210 free(branch); 211 return -1; 212 } 213 string_list_insert(&info->complete_reflogs, branch)->util 214 = reflogs; 215 } 216 free(branch); 217 218 CALLOC_ARRAY(commit_reflog, 1); 219 if (recno < 0) { 220 commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); 221 if (commit_reflog->recno < 0) { 222 free(commit_reflog); 223 return -1; 224 } 225 } else 226 commit_reflog->recno = reflogs->nr - recno - 1; 227 commit_reflog->selector = selector; 228 commit_reflog->reflogs = reflogs; 229 230 ALLOC_GROW(info->logs, info->nr + 1, info->alloc); 231 info->logs[info->nr++] = commit_reflog; 232 233 return 0; 234} 235 236void get_reflog_selector(struct strbuf *sb, 237 struct reflog_walk_info *reflog_info, 238 struct date_mode dmode, int force_date, 239 int shorten) 240{ 241 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; 242 struct reflog_info *info; 243 const char *printed_ref; 244 245 if (!commit_reflog) 246 return; 247 248 if (shorten) { 249 if (!commit_reflog->reflogs->short_ref) 250 commit_reflog->reflogs->short_ref 251 = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), 252 commit_reflog->reflogs->ref, 253 0); 254 printed_ref = commit_reflog->reflogs->short_ref; 255 } else { 256 printed_ref = commit_reflog->reflogs->ref; 257 } 258 259 strbuf_addf(sb, "%s@{", printed_ref); 260 if (commit_reflog->selector == SELECTOR_DATE || 261 (commit_reflog->selector == SELECTOR_NONE && force_date)) { 262 info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; 263 strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode)); 264 } else { 265 strbuf_addf(sb, "%d", commit_reflog->reflogs->nr 266 - 2 - commit_reflog->recno); 267 } 268 269 strbuf_addch(sb, '}'); 270} 271 272void get_reflog_message(struct strbuf *sb, 273 struct reflog_walk_info *reflog_info) 274{ 275 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; 276 struct reflog_info *info; 277 size_t len; 278 279 if (!commit_reflog) 280 return; 281 282 info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; 283 len = strlen(info->message); 284 if (len > 0) 285 len--; /* strip away trailing newline */ 286 strbuf_add(sb, info->message, len); 287} 288 289const char *get_reflog_ident(struct reflog_walk_info *reflog_info) 290{ 291 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; 292 struct reflog_info *info; 293 294 if (!commit_reflog) 295 return NULL; 296 297 info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; 298 return info->email; 299} 300 301timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info) 302{ 303 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; 304 struct reflog_info *info; 305 306 if (!commit_reflog) 307 return 0; 308 309 info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; 310 return info->timestamp; 311} 312 313void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline, 314 struct date_mode dmode, int force_date) 315{ 316 if (reflog_info && reflog_info->last_commit_reflog) { 317 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; 318 struct reflog_info *info; 319 struct strbuf selector = STRBUF_INIT; 320 321 info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; 322 get_reflog_selector(&selector, reflog_info, dmode, force_date, 0); 323 if (oneline) { 324 printf("%s: %s", selector.buf, info->message); 325 } 326 else { 327 printf("Reflog: %s (%s)\nReflog message: %s", 328 selector.buf, info->email, info->message); 329 } 330 331 strbuf_release(&selector); 332 } 333} 334 335int reflog_walk_empty(struct reflog_walk_info *info) 336{ 337 return !info || !info->nr; 338} 339 340static struct commit *next_reflog_commit(struct commit_reflog *log) 341{ 342 for (; log->recno >= 0; log->recno--) { 343 struct reflog_info *entry = &log->reflogs->items[log->recno]; 344 struct object *obj = parse_object(the_repository, 345 &entry->noid); 346 347 if (obj && obj->type == OBJ_COMMIT) 348 return (struct commit *)obj; 349 } 350 return NULL; 351} 352 353static timestamp_t log_timestamp(struct commit_reflog *log) 354{ 355 return log->reflogs->items[log->recno].timestamp; 356} 357 358struct commit *next_reflog_entry(struct reflog_walk_info *walk) 359{ 360 struct commit_reflog *best = NULL; 361 struct commit *best_commit = NULL; 362 size_t i; 363 364 for (i = 0; i < walk->nr; i++) { 365 struct commit_reflog *log = walk->logs[i]; 366 struct commit *commit = next_reflog_commit(log); 367 368 if (!commit) 369 continue; 370 371 if (!best || log_timestamp(log) > log_timestamp(best)) { 372 best = log; 373 best_commit = commit; 374 } 375 } 376 377 if (best) { 378 best->recno--; 379 walk->last_commit_reflog = best; 380 return best_commit; 381 } 382 383 return NULL; 384}