2006-07-06 21:16:22 +04:00
|
|
|
#include "cache.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
|
|
|
#include "builtin.h"
|
2007-01-06 13:16:17 +03:00
|
|
|
#include "reachable.h"
|
2008-03-23 23:50:29 +03:00
|
|
|
#include "parse-options.h"
|
2011-11-05 16:00:08 +04:00
|
|
|
#include "progress.h"
|
2006-07-06 21:16:22 +04:00
|
|
|
|
2008-03-23 23:50:29 +03:00
|
|
|
static const char * const prune_usage[] = {
|
2017-11-21 18:51:52 +03:00
|
|
|
N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
|
2008-03-23 23:50:29 +03:00
|
|
|
NULL
|
|
|
|
};
|
2006-08-15 21:23:48 +04:00
|
|
|
static int show_only;
|
2008-09-29 20:49:52 +04:00
|
|
|
static int verbose;
|
2017-04-26 22:29:31 +03:00
|
|
|
static timestamp_t expire;
|
2011-11-08 09:34:08 +04:00
|
|
|
static int show_progress = -1;
|
2006-07-06 21:16:22 +04:00
|
|
|
|
2013-12-18 03:22:31 +04:00
|
|
|
static int prune_tmp_file(const char *fullpath)
|
2008-07-25 02:41:12 +04:00
|
|
|
{
|
2010-02-27 06:50:02 +03:00
|
|
|
struct stat st;
|
|
|
|
if (lstat(fullpath, &st))
|
|
|
|
return error("Could not stat '%s'", fullpath);
|
|
|
|
if (st.st_mtime > expire)
|
|
|
|
return 0;
|
2012-08-07 09:01:49 +04:00
|
|
|
if (show_only || verbose)
|
|
|
|
printf("Removing stale temporary file %s\n", fullpath);
|
2008-07-25 02:41:12 +04:00
|
|
|
if (!show_only)
|
2009-04-30 01:22:56 +04:00
|
|
|
unlink_or_warn(fullpath);
|
2008-07-25 02:41:12 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-22 02:47:35 +03:00
|
|
|
static int prune_object(const struct object_id *oid, const char *fullpath,
|
2014-10-16 02:38:55 +04:00
|
|
|
void *data)
|
2006-07-06 21:16:22 +04:00
|
|
|
{
|
2010-02-27 06:50:02 +03:00
|
|
|
struct stat st;
|
2014-10-16 02:38:55 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we know about this object?
|
|
|
|
* It must have been reachable
|
|
|
|
*/
|
2017-02-22 02:47:35 +03:00
|
|
|
if (lookup_object(oid->hash))
|
2014-10-16 02:38:55 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (lstat(fullpath, &st)) {
|
|
|
|
/* report errors, but do not stop pruning */
|
|
|
|
error("Could not stat '%s'", fullpath);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-02-27 06:50:02 +03:00
|
|
|
if (st.st_mtime > expire)
|
|
|
|
return 0;
|
2008-09-29 20:49:52 +04:00
|
|
|
if (show_only || verbose) {
|
2018-04-25 21:20:59 +03:00
|
|
|
enum object_type type = oid_object_info(the_repository, oid,
|
|
|
|
NULL);
|
2017-02-22 02:47:35 +03:00
|
|
|
printf("%s %s\n", oid_to_hex(oid),
|
2018-02-14 21:59:24 +03:00
|
|
|
(type > 0) ? type_name(type) : "unknown");
|
2008-09-29 20:49:52 +04:00
|
|
|
}
|
|
|
|
if (!show_only)
|
2009-04-30 01:22:56 +04:00
|
|
|
unlink_or_warn(fullpath);
|
2006-07-06 21:16:22 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-16 02:38:55 +04:00
|
|
|
static int prune_cruft(const char *basename, const char *path, void *data)
|
2006-07-06 21:16:22 +04:00
|
|
|
{
|
2014-10-16 02:38:55 +04:00
|
|
|
if (starts_with(basename, "tmp_obj_"))
|
|
|
|
prune_tmp_file(path);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "bad sha1 file: %s\n", path);
|
2006-07-06 21:16:22 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-24 17:09:39 +03:00
|
|
|
static int prune_subdir(unsigned int nr, const char *path, void *data)
|
2006-07-06 21:16:22 +04:00
|
|
|
{
|
2014-10-16 02:38:55 +04:00
|
|
|
if (!show_only)
|
|
|
|
rmdir(path);
|
|
|
|
return 0;
|
2006-07-06 21:16:22 +04:00
|
|
|
}
|
|
|
|
|
2008-02-07 05:55:14 +03:00
|
|
|
/*
|
|
|
|
* Write errors (particularly out of space) can result in
|
|
|
|
* failed temporary packs (and more rarely indexes and other
|
2010-02-04 08:23:18 +03:00
|
|
|
* files beginning with "tmp_") accumulating in the object
|
2008-09-23 03:34:26 +04:00
|
|
|
* and the pack directories.
|
2008-02-07 05:55:14 +03:00
|
|
|
*/
|
2008-09-23 03:34:26 +04:00
|
|
|
static void remove_temporary_files(const char *path)
|
2008-02-07 05:55:14 +03:00
|
|
|
{
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *de;
|
|
|
|
|
2008-09-23 03:34:26 +04:00
|
|
|
dir = opendir(path);
|
2008-02-07 05:55:14 +03:00
|
|
|
if (!dir) {
|
2008-09-23 03:34:26 +04:00
|
|
|
fprintf(stderr, "Unable to open directory %s\n", path);
|
2008-02-07 05:55:14 +03:00
|
|
|
return;
|
|
|
|
}
|
2008-07-25 02:41:12 +04:00
|
|
|
while ((de = readdir(dir)) != NULL)
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(de->d_name, "tmp_"))
|
2013-12-18 03:22:31 +04:00
|
|
|
prune_tmp_file(mkpath("%s/%s", path, de->d_name));
|
2008-02-07 05:55:14 +03:00
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
|
2006-07-29 09:44:25 +04:00
|
|
|
int cmd_prune(int argc, const char **argv, const char *prefix)
|
2006-07-06 21:16:22 +04:00
|
|
|
{
|
2007-01-06 13:16:10 +03:00
|
|
|
struct rev_info revs;
|
2011-11-08 09:34:08 +04:00
|
|
|
struct progress *progress = NULL;
|
2017-12-08 18:27:16 +03:00
|
|
|
int exclude_promisor_objects = 0;
|
2008-03-23 23:50:29 +03:00
|
|
|
const struct option options[] = {
|
2012-08-20 16:32:32 +04:00
|
|
|
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
|
|
|
|
OPT__VERBOSE(&verbose, N_("report pruned objects")),
|
|
|
|
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
|
2013-04-25 22:13:49 +04:00
|
|
|
OPT_EXPIRY_DATE(0, "expire", &expire,
|
|
|
|
N_("expire objects older than <time>")),
|
2017-12-08 18:27:16 +03:00
|
|
|
OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
|
|
|
|
N_("limit traversal to objects outside promisor packfiles")),
|
2008-03-23 23:50:29 +03:00
|
|
|
OPT_END()
|
|
|
|
};
|
2008-09-23 03:34:26 +04:00
|
|
|
char *s;
|
2006-07-06 21:16:22 +04:00
|
|
|
|
2017-04-26 22:29:31 +03:00
|
|
|
expire = TIME_MAX;
|
2007-01-06 00:31:43 +03:00
|
|
|
save_commit_buffer = 0;
|
2014-02-18 15:24:55 +04:00
|
|
|
check_replace_refs = 0;
|
2015-03-20 21:43:09 +03:00
|
|
|
ref_paranoia = 1;
|
2006-07-29 09:44:25 +04:00
|
|
|
init_revisions(&revs, prefix);
|
2006-07-06 21:16:22 +04:00
|
|
|
|
2009-05-23 22:53:12 +04:00
|
|
|
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
|
2014-11-30 11:24:48 +03:00
|
|
|
|
2015-06-23 13:54:11 +03:00
|
|
|
if (repository_format_precious_objects)
|
|
|
|
die(_("cannot prune in a precious-objects repo"));
|
|
|
|
|
2008-03-25 09:20:51 +03:00
|
|
|
while (argc--) {
|
2017-05-01 05:28:58 +03:00
|
|
|
struct object_id oid;
|
2008-03-25 09:20:51 +03:00
|
|
|
const char *name = *argv++;
|
|
|
|
|
2017-05-01 05:28:58 +03:00
|
|
|
if (!get_oid(name, &oid)) {
|
object: convert parse_object* to take struct object_id
Make parse_object, parse_object_or_die, and parse_object_buffer take a
pointer to struct object_id. Remove the temporary variables inserted
earlier, since they are no longer necessary. Transform all of the
callers using the following semantic patch:
@@
expression E1;
@@
- parse_object(E1.hash)
+ parse_object(&E1)
@@
expression E1;
@@
- parse_object(E1->hash)
+ parse_object(E1)
@@
expression E1, E2;
@@
- parse_object_or_die(E1.hash, E2)
+ parse_object_or_die(&E1, E2)
@@
expression E1, E2;
@@
- parse_object_or_die(E1->hash, E2)
+ parse_object_or_die(E1, E2)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1.hash, E2, E3, E4, E5)
+ parse_object_buffer(&E1, E2, E3, E4, E5)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1->hash, E2, E3, E4, E5)
+ parse_object_buffer(E1, E2, E3, E4, E5)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 01:10:38 +03:00
|
|
|
struct object *object = parse_object_or_die(&oid,
|
|
|
|
name);
|
2008-03-25 09:20:51 +03:00
|
|
|
add_pending_object(&revs, object, "");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
die("unrecognized argument: %s", name);
|
|
|
|
}
|
2011-11-08 09:34:08 +04:00
|
|
|
|
|
|
|
if (show_progress == -1)
|
|
|
|
show_progress = isatty(2);
|
|
|
|
if (show_progress)
|
progress: simplify "delayed" progress API
We used to expose the full power of the delayed progress API to the
callers, so that they can specify, not just the message to show and
expected total amount of work that is used to compute the percentage
of work performed so far, the percent-threshold parameter P and the
delay-seconds parameter N. The progress meter starts to show at N
seconds into the operation only if we have not yet completed P per-cent
of the total work.
Most callers used either (0%, 2s) or (50%, 1s) as (P, N), but there
are oddballs that chose more random-looking values like 95%.
For a smoother workload, (50%, 1s) would allow us to start showing
the progress meter earlier than (0%, 2s), while keeping the chance
of not showing progress meter for long running operation the same as
the latter. For a task that would take 2s or more to complete, it
is likely that less than half of it would complete within the first
second, if the workload is smooth. But for a spiky workload whose
earlier part is easier, such a setting is likely to fail to show the
progress meter entirely and (0%, 2s) is more appropriate.
But that is merely a theory. Realistically, it is of dubious value
to ask each codepath to carefully consider smoothness of their
workload and specify their own setting by passing two extra
parameters. Let's simplify the API by dropping both parameters and
have everybody use (0%, 2s).
Oh, by the way, the percent-threshold parameter and the structure
member were consistently misspelled, which also is now fixed ;-)
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 20:39:41 +03:00
|
|
|
progress = start_delayed_progress(_("Checking connectivity"), 0);
|
2017-12-08 18:27:16 +03:00
|
|
|
if (exclude_promisor_objects) {
|
|
|
|
fetch_if_missing = 0;
|
|
|
|
revs.exclude_promisor_objects = 1;
|
|
|
|
}
|
2011-11-08 09:34:08 +04:00
|
|
|
|
prune: keep objects reachable from recent objects
Our current strategy with prune is that an object falls into
one of three categories:
1. Reachable (from ref tips, reflogs, index, etc).
2. Not reachable, but recent (based on the --expire time).
3. Not reachable and not recent.
We keep objects from (1) and (2), but prune objects in (3).
The point of (2) is that these objects may be part of an
in-progress operation that has not yet updated any refs.
However, it is not always the case that objects for an
in-progress operation will have a recent mtime. For example,
the object database may have an old copy of a blob (from an
abandoned operation, a branch that was deleted, etc). If we
create a new tree that points to it, a simultaneous prune
will leave our tree, but delete the blob. Referencing that
tree with a commit will then work (we check that the tree is
in the object database, but not that all of its referred
objects are), as will mentioning the commit in a ref. But
the resulting repo is corrupt; we are missing the blob
reachable from a ref.
One way to solve this is to be more thorough when
referencing a sha1: make sure that not only do we have that
sha1, but that we have objects it refers to, and so forth
recursively. The problem is that this is very expensive.
Creating a parent link would require traversing the entire
object graph!
Instead, this patch pushes the extra work onto prune, which
runs less frequently (and has to look at the whole object
graph anyway). It creates a new category of objects: objects
which are not recent, but which are reachable from a recent
object. We do not prune these objects, just like the
reachable and recent ones.
This lets us avoid the recursive check above, because if we
have an object, even if it is unreachable, we should have
its referent. We can make a simple inductive argument that
with this patch, this property holds (that there are no
objects with missing referents in the repository):
0. When we have no objects, we have nothing to refer or be
referred to, so the property holds.
1. If we add objects to the repository, their direct
referents must generally exist (e.g., if you create a
tree, the blobs it references must exist; if you create
a commit to point at the tree, the tree must exist).
This is already the case before this patch. And it is
not 100% foolproof (you can make bogus objects using
`git hash-object`, for example), but it should be the
case for normal usage.
Therefore for any sequence of object additions, the
property will continue to hold.
2. If we remove objects from the repository, then we will
not remove a child object (like a blob) if an object
that refers to it is being kept. That is the part
implemented by this patch.
Note, however, that our reachability check and the
actual pruning are not atomic. So it _is_ still
possible to violate the property (e.g., an object
becomes referenced just as we are deleting it). This
patch is shooting for eliminating problems where the
mtimes of dependent objects differ by hours or days,
and one is dropped without the other. It does nothing
to help with short races.
Naively, the simplest way to implement this would be to add
all recent objects as tips to the reachability traversal.
However, this does not perform well. In a recently-packed
repository, all reachable objects will also be recent, and
therefore we have to look at each object twice. This patch
instead performs the reachability traversal, then follows up
with a second traversal for recent objects, skipping any
that have already been marked.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-10-16 02:41:35 +04:00
|
|
|
mark_reachable_objects(&revs, 1, expire, progress);
|
2011-11-05 16:00:08 +04:00
|
|
|
stop_progress(&progress);
|
2014-10-16 02:38:55 +04:00
|
|
|
for_each_loose_file_in_objdir(get_object_directory(), prune_object,
|
|
|
|
prune_cruft, prune_subdir, NULL);
|
2006-07-06 21:16:22 +04:00
|
|
|
|
2013-05-27 15:18:47 +04:00
|
|
|
prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
|
2008-09-23 03:34:26 +04:00
|
|
|
remove_temporary_files(get_object_directory());
|
2012-09-04 21:31:14 +04:00
|
|
|
s = mkpathdup("%s/pack", get_object_directory());
|
2008-09-23 03:34:26 +04:00
|
|
|
remove_temporary_files(s);
|
|
|
|
free(s);
|
2013-12-05 17:02:54 +04:00
|
|
|
|
|
|
|
if (is_repository_shallow())
|
|
|
|
prune_shallow(show_only);
|
|
|
|
|
2006-07-06 21:16:22 +04:00
|
|
|
return 0;
|
|
|
|
}
|