Merge branch 'nd/tree-walk-path-exclusion'
[git] / reachable.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "tag.h"
4 #include "commit.h"
5 #include "blob.h"
6 #include "diff.h"
7 #include "revision.h"
8 #include "reachable.h"
9 #include "cache-tree.h"
10 #include "progress.h"
11 #include "list-objects.h"
12 #include "packfile.h"
13 #include "worktree.h"
14 #include "object-store.h"
15
16 struct connectivity_progress {
17         struct progress *progress;
18         unsigned long count;
19 };
20
21 static void update_progress(struct connectivity_progress *cp)
22 {
23         cp->count++;
24         if ((cp->count & 1023) == 0)
25                 display_progress(cp->progress, cp->count);
26 }
27
28 static int add_one_ref(const char *path, const struct object_id *oid,
29                        int flag, void *cb_data)
30 {
31         struct rev_info *revs = (struct rev_info *)cb_data;
32         struct object *object;
33
34         if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
35                 warning("symbolic ref is dangling: %s", path);
36                 return 0;
37         }
38
39         object = parse_object_or_die(oid, path);
40         add_pending_object(revs, object, "");
41
42         return 0;
43 }
44
45 /*
46  * The traversal will have already marked us as SEEN, so we
47  * only need to handle any progress reporting here.
48  */
49 static void mark_object(struct object *obj, const char *name, void *data)
50 {
51         update_progress(data);
52 }
53
54 static void mark_commit(struct commit *c, void *data)
55 {
56         mark_object(&c->object, NULL, data);
57 }
58
59 struct recent_data {
60         struct rev_info *revs;
61         timestamp_t timestamp;
62 };
63
64 static void add_recent_object(const struct object_id *oid,
65                               timestamp_t mtime,
66                               struct recent_data *data)
67 {
68         struct object *obj;
69         enum object_type type;
70
71         if (mtime <= data->timestamp)
72                 return;
73
74         /*
75          * We do not want to call parse_object here, because
76          * inflating blobs and trees could be very expensive.
77          * However, we do need to know the correct type for
78          * later processing, and the revision machinery expects
79          * commits and tags to have been parsed.
80          */
81         type = oid_object_info(the_repository, oid, NULL);
82         if (type < 0)
83                 die("unable to get object info for %s", oid_to_hex(oid));
84
85         switch (type) {
86         case OBJ_TAG:
87         case OBJ_COMMIT:
88                 obj = parse_object_or_die(oid, NULL);
89                 break;
90         case OBJ_TREE:
91                 obj = (struct object *)lookup_tree(the_repository, oid);
92                 break;
93         case OBJ_BLOB:
94                 obj = (struct object *)lookup_blob(the_repository, oid);
95                 break;
96         default:
97                 die("unknown object type for %s: %s",
98                     oid_to_hex(oid), type_name(type));
99         }
100
101         if (!obj)
102                 die("unable to lookup %s", oid_to_hex(oid));
103
104         add_pending_object(data->revs, obj, "");
105 }
106
107 static int add_recent_loose(const struct object_id *oid,
108                             const char *path, void *data)
109 {
110         struct stat st;
111         struct object *obj = lookup_object(the_repository, oid->hash);
112
113         if (obj && obj->flags & SEEN)
114                 return 0;
115
116         if (stat(path, &st) < 0) {
117                 /*
118                  * It's OK if an object went away during our iteration; this
119                  * could be due to a simultaneous repack. But anything else
120                  * we should abort, since we might then fail to mark objects
121                  * which should not be pruned.
122                  */
123                 if (errno == ENOENT)
124                         return 0;
125                 return error_errno("unable to stat %s", oid_to_hex(oid));
126         }
127
128         add_recent_object(oid, st.st_mtime, data);
129         return 0;
130 }
131
132 static int add_recent_packed(const struct object_id *oid,
133                              struct packed_git *p, uint32_t pos,
134                              void *data)
135 {
136         struct object *obj = lookup_object(the_repository, oid->hash);
137
138         if (obj && obj->flags & SEEN)
139                 return 0;
140         add_recent_object(oid, p->mtime, data);
141         return 0;
142 }
143
144 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
145                                            timestamp_t timestamp)
146 {
147         struct recent_data data;
148         int r;
149
150         data.revs = revs;
151         data.timestamp = timestamp;
152
153         r = for_each_loose_object(add_recent_loose, &data,
154                                   FOR_EACH_OBJECT_LOCAL_ONLY);
155         if (r)
156                 return r;
157         return for_each_packed_object(add_recent_packed, &data,
158                                       FOR_EACH_OBJECT_LOCAL_ONLY);
159 }
160
161 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
162                             timestamp_t mark_recent, struct progress *progress)
163 {
164         struct connectivity_progress cp;
165
166         /*
167          * Set up revision parsing, and mark us as being interested
168          * in all object types, not just commits.
169          */
170         revs->tag_objects = 1;
171         revs->blob_objects = 1;
172         revs->tree_objects = 1;
173
174         /* Add all refs from the index file */
175         add_index_objects_to_pending(revs, 0);
176
177         /* Add all external refs */
178         for_each_ref(add_one_ref, revs);
179
180         /* detached HEAD is not included in the list above */
181         head_ref(add_one_ref, revs);
182         other_head_refs(add_one_ref, revs);
183
184         /* Add all reflog info */
185         if (mark_reflog)
186                 add_reflogs_to_pending(revs, 0);
187
188         cp.progress = progress;
189         cp.count = 0;
190
191         /*
192          * Set up the revision walk - this will move all commits
193          * from the pending list to the commit walking list.
194          */
195         if (prepare_revision_walk(revs))
196                 die("revision walk setup failed");
197         traverse_commit_list(revs, mark_commit, mark_object, &cp);
198
199         if (mark_recent) {
200                 revs->ignore_missing_links = 1;
201                 if (add_unseen_recent_objects_to_traversal(revs, mark_recent))
202                         die("unable to mark recent objects");
203                 if (prepare_revision_walk(revs))
204                         die("revision walk setup failed");
205                 traverse_commit_list(revs, mark_commit, mark_object, &cp);
206         }
207
208         display_progress(cp.progress, cp.count);
209 }