8 static unsigned long default_reflog_expire;
9 static unsigned long default_reflog_expire_unreachable;
11 struct expire_reflog_cb {
14 struct commit *ref_commit;
15 unsigned long expire_total;
16 unsigned long expire_unreachable;
19 static int tree_is_complete(const unsigned char *sha1)
21 struct tree_desc desc;
25 buf = read_sha1_file(sha1, type, &desc.size);
30 const unsigned char *elem;
34 elem = tree_entry_extract(&desc, &name, &mode);
35 if (!has_sha1_file(elem) ||
36 (S_ISDIR(mode) && !tree_is_complete(elem))) {
40 update_tree_entry(&desc);
46 static int keep_entry(struct commit **it, unsigned char *sha1)
48 struct commit *commit;
51 if (is_null_sha1(sha1))
53 commit = lookup_commit_reference_gently(sha1, 1);
57 /* Make sure everything in this commit exists. */
58 parse_object(commit->object.sha1);
59 if (!tree_is_complete(commit->tree->object.sha1))
65 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
66 char *data, void *cb_data)
68 struct expire_reflog_cb *cb = cb_data;
69 unsigned long timestamp;
71 struct commit *old, *new;
73 cp = strchr(data, '>');
74 if (!cp || *++cp != ' ')
76 timestamp = strtoul(cp, &ep, 10);
79 if (timestamp < cb->expire_total)
82 if (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))
85 if ((timestamp < cb->expire_unreachable) &&
87 (old && !in_merge_bases(old, cb->ref_commit)) ||
88 (new && !in_merge_bases(new, cb->ref_commit))))
92 fprintf(cb->newlog, "%s %s %s",
93 sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
97 fprintf(stderr, "would prune %s", data);
101 struct cmd_reflog_expire_cb {
103 unsigned long expire_total;
104 unsigned long expire_unreachable;
107 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
109 struct cmd_reflog_expire_cb *cmd = cb_data;
110 struct expire_reflog_cb cb;
111 struct ref_lock *lock;
112 char *newlog_path = NULL;
115 if (strncmp(ref, "refs/", 5))
116 return error("not a ref '%s'", ref);
118 memset(&cb, 0, sizeof(cb));
119 /* we take the lock for the ref itself to prevent it from
122 lock = lock_ref_sha1(ref + 5, sha1);
124 return error("cannot lock ref '%s'", ref);
125 if (!file_exists(lock->log_file))
128 newlog_path = xstrdup(git_path("logs/%s.lock", ref));
129 cb.newlog = fopen(newlog_path, "w");
132 cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
135 "warning: ref '%s' does not point at a commit\n", ref);
137 cb.expire_total = cmd->expire_total;
138 cb.expire_unreachable = cmd->expire_unreachable;
139 for_each_reflog_ent(ref, expire_reflog_ent, &cb);
142 if (fclose(cb.newlog))
143 status |= error("%s: %s", strerror(errno),
145 if (rename(newlog_path, lock->log_file)) {
146 status |= error("cannot rename %s to %s",
147 newlog_path, lock->log_file);
156 static int reflog_expire_config(const char *var, const char *value)
158 if (!strcmp(var, "gc.reflogexpire"))
159 default_reflog_expire = approxidate(value);
160 else if (!strcmp(var, "gc.reflogexpireunreachable"))
161 default_reflog_expire_unreachable = approxidate(value);
163 return git_default_config(var, value);
167 static const char reflog_expire_usage[] =
168 "git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
170 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
172 struct cmd_reflog_expire_cb cb;
173 unsigned long now = time(NULL);
174 int i, status, do_all;
176 git_config(reflog_expire_config);
178 save_commit_buffer = 0;
180 memset(&cb, 0, sizeof(cb));
182 if (!default_reflog_expire_unreachable)
183 default_reflog_expire_unreachable = now - 30 * 24 * 3600;
184 if (!default_reflog_expire)
185 default_reflog_expire = now - 90 * 24 * 3600;
186 cb.expire_total = default_reflog_expire;
187 cb.expire_unreachable = default_reflog_expire_unreachable;
189 for (i = 1; i < argc; i++) {
190 const char *arg = argv[i];
191 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
193 else if (!strncmp(arg, "--expire=", 9))
194 cb.expire_total = approxidate(arg + 9);
195 else if (!strncmp(arg, "--expire-unreachable=", 21))
196 cb.expire_unreachable = approxidate(arg + 21);
197 else if (!strcmp(arg, "--all"))
199 else if (!strcmp(arg, "--")) {
203 else if (arg[0] == '-')
204 usage(reflog_expire_usage);
209 status |= for_each_ref(expire_reflog, &cb);
211 const char *ref = argv[i++];
212 unsigned char sha1[20];
213 if (!resolve_ref(ref, sha1, 1, NULL)) {
214 status |= error("%s points nowhere!", ref);
217 status |= expire_reflog(ref, sha1, 0, &cb);
222 static const char reflog_usage[] =
223 "git-reflog (expire | ...)";
225 int cmd_reflog(int argc, const char **argv, const char *prefix)
229 else if (!strcmp(argv[1], "expire"))
230 return cmd_reflog_expire(argc - 1, argv + 1, prefix);