Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/prune.c
Line
Count
Source (jump to first uncovered line)
1
#include "builtin.h"
2
#include "commit.h"
3
#include "diff.h"
4
#include "dir.h"
5
#include "environment.h"
6
#include "gettext.h"
7
#include "hex.h"
8
#include "revision.h"
9
#include "reachable.h"
10
#include "parse-options.h"
11
#include "path.h"
12
#include "progress.h"
13
#include "prune-packed.h"
14
#include "replace-object.h"
15
#include "object-file.h"
16
#include "object-name.h"
17
#include "object-store-ll.h"
18
#include "shallow.h"
19
20
static const char * const prune_usage[] = {
21
  N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
22
  NULL
23
};
24
static int show_only;
25
static int verbose;
26
static timestamp_t expire;
27
static int show_progress = -1;
28
29
static int prune_tmp_file(const char *fullpath)
30
0
{
31
0
  struct stat st;
32
0
  if (lstat(fullpath, &st))
33
0
    return error("Could not stat '%s'", fullpath);
34
0
  if (st.st_mtime > expire)
35
0
    return 0;
36
0
  if (S_ISDIR(st.st_mode)) {
37
0
    if (show_only || verbose)
38
0
      printf("Removing stale temporary directory %s\n", fullpath);
39
0
    if (!show_only) {
40
0
      struct strbuf remove_dir_buf = STRBUF_INIT;
41
42
0
      strbuf_addstr(&remove_dir_buf, fullpath);
43
0
      remove_dir_recursively(&remove_dir_buf, 0);
44
0
      strbuf_release(&remove_dir_buf);
45
0
    }
46
0
  } else {
47
0
    if (show_only || verbose)
48
0
      printf("Removing stale temporary file %s\n", fullpath);
49
0
    if (!show_only)
50
0
      unlink_or_warn(fullpath);
51
0
  }
52
0
  return 0;
53
0
}
54
55
static void perform_reachability_traversal(struct rev_info *revs)
56
0
{
57
0
  static int initialized;
58
0
  struct progress *progress = NULL;
59
60
0
  if (initialized)
61
0
    return;
62
63
0
  if (show_progress)
64
0
    progress = start_delayed_progress(_("Checking connectivity"), 0);
65
0
  mark_reachable_objects(revs, 1, expire, progress);
66
0
  stop_progress(&progress);
67
0
  initialized = 1;
68
0
}
69
70
static int is_object_reachable(const struct object_id *oid,
71
             struct rev_info *revs)
72
0
{
73
0
  struct object *obj;
74
75
0
  perform_reachability_traversal(revs);
76
77
0
  obj = lookup_object(the_repository, oid);
78
0
  return obj && (obj->flags & SEEN);
79
0
}
80
81
static int prune_object(const struct object_id *oid, const char *fullpath,
82
      void *data)
83
0
{
84
0
  struct rev_info *revs = data;
85
0
  struct stat st;
86
87
0
  if (is_object_reachable(oid, revs))
88
0
    return 0;
89
90
0
  if (lstat(fullpath, &st)) {
91
    /* report errors, but do not stop pruning */
92
0
    error("Could not stat '%s'", fullpath);
93
0
    return 0;
94
0
  }
95
0
  if (st.st_mtime > expire)
96
0
    return 0;
97
0
  if (show_only || verbose) {
98
0
    enum object_type type = oid_object_info(the_repository, oid,
99
0
              NULL);
100
0
    printf("%s %s\n", oid_to_hex(oid),
101
0
           (type > 0) ? type_name(type) : "unknown");
102
0
  }
103
0
  if (!show_only)
104
0
    unlink_or_warn(fullpath);
105
0
  return 0;
106
0
}
107
108
static int prune_cruft(const char *basename, const char *path,
109
           void *data UNUSED)
110
0
{
111
0
  if (starts_with(basename, "tmp_obj_"))
112
0
    prune_tmp_file(path);
113
0
  else
114
0
    fprintf(stderr, "bad sha1 file: %s\n", path);
115
0
  return 0;
116
0
}
117
118
static int prune_subdir(unsigned int nr UNUSED, const char *path,
119
      void *data UNUSED)
120
0
{
121
0
  if (!show_only)
122
0
    rmdir(path);
123
0
  return 0;
124
0
}
125
126
/*
127
 * Write errors (particularly out of space) can result in
128
 * failed temporary packs (and more rarely indexes and other
129
 * files beginning with "tmp_") accumulating in the object
130
 * and the pack directories.
131
 */
132
static void remove_temporary_files(const char *path)
133
0
{
134
0
  DIR *dir;
135
0
  struct dirent *de;
136
137
0
  dir = opendir(path);
138
0
  if (!dir) {
139
0
    if (errno != ENOENT)
140
0
      fprintf(stderr, "Unable to open directory %s: %s\n",
141
0
        path, strerror(errno));
142
0
    return;
143
0
  }
144
0
  while ((de = readdir(dir)) != NULL)
145
0
    if (starts_with(de->d_name, "tmp_"))
146
0
      prune_tmp_file(mkpath("%s/%s", path, de->d_name));
147
0
  closedir(dir);
148
0
}
149
150
int cmd_prune(int argc, const char **argv, const char *prefix)
151
0
{
152
0
  struct rev_info revs;
153
0
  int exclude_promisor_objects = 0;
154
0
  const struct option options[] = {
155
0
    OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
156
0
    OPT__VERBOSE(&verbose, N_("report pruned objects")),
157
0
    OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
158
0
    OPT_EXPIRY_DATE(0, "expire", &expire,
159
0
        N_("expire objects older than <time>")),
160
0
    OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
161
0
       N_("limit traversal to objects outside promisor packfiles")),
162
0
    OPT_END()
163
0
  };
164
0
  char *s;
165
166
0
  expire = TIME_MAX;
167
0
  save_commit_buffer = 0;
168
0
  disable_replace_refs();
169
0
  repo_init_revisions(the_repository, &revs, prefix);
170
171
0
  argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
172
173
0
  if (repository_format_precious_objects)
174
0
    die(_("cannot prune in a precious-objects repo"));
175
176
0
  while (argc--) {
177
0
    struct object_id oid;
178
0
    const char *name = *argv++;
179
180
0
    if (!repo_get_oid(the_repository, name, &oid)) {
181
0
      struct object *object = parse_object_or_die(&oid,
182
0
                    name);
183
0
      add_pending_object(&revs, object, "");
184
0
    }
185
0
    else
186
0
      die("unrecognized argument: %s", name);
187
0
  }
188
189
0
  if (show_progress == -1)
190
0
    show_progress = isatty(2);
191
0
  if (exclude_promisor_objects) {
192
0
    fetch_if_missing = 0;
193
0
    revs.exclude_promisor_objects = 1;
194
0
  }
195
196
0
  for_each_loose_file_in_objdir(get_object_directory(), prune_object,
197
0
              prune_cruft, prune_subdir, &revs);
198
199
0
  prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
200
0
  remove_temporary_files(get_object_directory());
201
0
  s = mkpathdup("%s/pack", get_object_directory());
202
0
  remove_temporary_files(s);
203
0
  free(s);
204
205
0
  if (is_repository_shallow(the_repository)) {
206
0
    perform_reachability_traversal(&revs);
207
0
    prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
208
0
  }
209
210
0
  release_revisions(&revs);
211
0
  return 0;
212
0
}