Code

Fix --walk-reflog with --pretty=oneline
[git.git] / reflog-walk.c
1 #include "cache.h"
2 #include "commit.h"
3 #include "refs.h"
4 #include "diff.h"
5 #include "revision.h"
6 #include "path-list.h"
7 #include "reflog-walk.h"
9 struct complete_reflogs {
10         char *ref;
11         struct reflog_info {
12                 unsigned char osha1[20], nsha1[20];
13                 char *email;
14                 unsigned long timestamp;
15                 int tz;
16                 char *message;
17         } *items;
18         int nr, alloc;
19 };
21 static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
22                 const char *email, unsigned long timestamp, int tz,
23                 const char *message, void *cb_data)
24 {
25         struct complete_reflogs *array = cb_data;
26         struct reflog_info *item;
28         if (array->nr >= array->alloc) {
29                 array->alloc = alloc_nr(array->nr + 1);
30                 array->items = xrealloc(array->items, array->alloc *
31                         sizeof(struct reflog_info));
32         }
33         item = array->items + array->nr;
34         memcpy(item->osha1, osha1, 20);
35         memcpy(item->nsha1, nsha1, 20);
36         item->email = xstrdup(email);
37         item->timestamp = timestamp;
38         item->tz = tz;
39         item->message = xstrdup(message);
40         array->nr++;
41         return 0;
42 }
44 static struct complete_reflogs *read_complete_reflog(const char *ref)
45 {
46         struct complete_reflogs *reflogs =
47                 xcalloc(sizeof(struct complete_reflogs), 1);
48         reflogs->ref = xstrdup(ref);
49         for_each_reflog_ent(ref, read_one_reflog, reflogs);
50         if (reflogs->nr == 0) {
51                 unsigned char sha1[20];
52                 const char *name = resolve_ref(ref, sha1, 1, NULL);
53                 if (name)
54                         for_each_reflog_ent(name, read_one_reflog, reflogs);
55         }
56         if (reflogs->nr == 0) {
57                 int len = strlen(ref);
58                 char *refname = xmalloc(len + 12);
59                 sprintf(refname, "refs/%s", ref);
60                 for_each_reflog_ent(refname, read_one_reflog, reflogs);
61                 if (reflogs->nr == 0) {
62                         sprintf(refname, "refs/heads/%s", ref);
63                         for_each_reflog_ent(refname, read_one_reflog, reflogs);
64                 }
65                 free(refname);
66         }
67         return reflogs;
68 }
70 static int get_reflog_recno_by_time(struct complete_reflogs *array,
71         unsigned long timestamp)
72 {
73         int i;
74         for (i = array->nr - 1; i >= 0; i++)
75                 if (timestamp >= array->items[i].timestamp)
76                         return i;
77         return -1;
78 }
80 struct commit_info_lifo {
81         struct commit_info {
82                 struct commit *commit;
83                 void *util;
84         } *items;
85         int nr, alloc;
86 };
88 static struct commit_info *get_commit_info(struct commit *commit,
89                 struct commit_info_lifo *lifo, int pop)
90 {
91         int i;
92         for (i = 0; i < lifo->nr; i++)
93                 if (lifo->items[i].commit == commit) {
94                         struct commit_info *result = &lifo->items[i];
95                         if (pop) {
96                                 if (i + 1 < lifo->nr)
97                                         memmove(lifo->items + i,
98                                                 lifo->items + i + 1,
99                                                 (lifo->nr - i) *
100                                                 sizeof(struct commit_info));
101                                 lifo->nr--;
102                         }
103                         return result;
104                 }
105         return NULL;
108 static void add_commit_info(struct commit *commit, void *util,
109                 struct commit_info_lifo *lifo)
111         struct commit_info *info;
112         if (lifo->nr >= lifo->alloc) {
113                 lifo->alloc = alloc_nr(lifo->nr + 1);
114                 lifo->items = xrealloc(lifo->items,
115                         lifo->alloc * sizeof(struct commit_info));
116         }
117         info = lifo->items + lifo->nr;
118         info->commit = commit;
119         info->util = util;
120         lifo->nr++;
123 struct commit_reflog {
124         int flag, recno;
125         struct complete_reflogs *reflogs;
126 };
128 struct reflog_walk_info {
129         struct commit_info_lifo reflogs;
130         struct path_list complete_reflogs;
131         struct commit_reflog *last_commit_reflog;
132 };
134 void init_reflog_walk(struct reflog_walk_info** info)
136         *info = xcalloc(sizeof(struct reflog_walk_info), 1);
139 void add_reflog_for_walk(struct reflog_walk_info *info,
140                 struct commit *commit, const char *name)
142         unsigned long timestamp = 0;
143         int recno = -1;
144         struct path_list_item *item;
145         struct complete_reflogs *reflogs;
146         char *branch, *at = strchr(name, '@');
147         struct commit_reflog *commit_reflog;
149         if (commit->object.flags & UNINTERESTING)
150                 die ("Cannot walk reflogs for %s", name);
152         branch = xstrdup(name);
153         if (at && at[1] == '{') {
154                 char *ep;
155                 branch[at - name] = '\0';
156                 recno = strtoul(at + 2, &ep, 10);
157                 if (*ep != '}') {
158                         recno = -1;
159                         timestamp = approxidate(at + 2);
160                 }
161         } else
162                 recno = 0;
164         item = path_list_lookup(branch, &info->complete_reflogs);
165         if (item)
166                 reflogs = item->util;
167         else {
168                 reflogs = read_complete_reflog(branch);
169                 if (!reflogs || reflogs->nr == 0)
170                         die("No reflogs found for '%s'", branch);
171                 path_list_insert(branch, &info->complete_reflogs)->util
172                         = reflogs;
173         }
175         commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
176         if (recno < 0) {
177                 commit_reflog->flag = 1;
178                 commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
179                 if (commit_reflog->recno < 0) {
180                         free(branch);
181                         free(commit_reflog);
182                         return;
183                 }
184         } else
185                 commit_reflog->recno = reflogs->nr - recno - 1;
186         commit_reflog->reflogs = reflogs;
188         add_commit_info(commit, commit_reflog, &info->reflogs);
191 void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
193         struct commit_info *commit_info =
194                 get_commit_info(commit, &info->reflogs, 0);
195         struct commit_reflog *commit_reflog;
196         struct reflog_info *reflog;
198         info->last_commit_reflog = NULL;
199         if (!commit_info)
200                 return;
202         commit_reflog = commit_info->util;
203         if (commit_reflog->recno < 0) {
204                 commit->parents = NULL;
205                 return;
206         }
208         reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
209         info->last_commit_reflog = commit_reflog;
210         commit_reflog->recno--;
211         commit_info->commit = (struct commit *)parse_object(reflog->osha1);
212         if (!commit_info->commit) {
213                 commit->parents = NULL;
214                 return;
215         }
217         commit->parents = xcalloc(sizeof(struct commit_list), 1);
218         commit->parents->item = commit_info->commit;
219         commit->object.flags &= ~(ADDED | SEEN | SHOWN);
222 void show_reflog_message(struct reflog_walk_info* info, int oneline)
224         if (info && info->last_commit_reflog) {
225                 struct commit_reflog *commit_reflog = info->last_commit_reflog;
226                 struct reflog_info *info;
228                 info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
229                 if (oneline) {
230                         printf("%s@{", commit_reflog->reflogs->ref);
231                         if (commit_reflog->flag)
232                                 printf("%s", show_date(info->timestamp, 0, 1));
233                         else
234                                 printf("%d", commit_reflog->reflogs->nr
235                                        - 2 - commit_reflog->recno);
236                         printf("}: ");
237                 }
238                 else {
239                         printf("Reflog: %s@{", commit_reflog->reflogs->ref);
240                         if (commit_reflog->flag)
241                                 printf("%s", show_rfc2822_date(info->timestamp,
242                                                                info->tz));
243                         else
244                                 printf("%d", commit_reflog->reflogs->nr
245                                        - 2 - commit_reflog->recno);
246                         printf("} (%s)\nReflog message: %s",
247                                info->email, info->message);
248                 }
249         }