Coverage Report

Created: 2023-11-19 07:08

/src/git/builtin/fast-export.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * "git fast-export" builtin command
3
 *
4
 * Copyright (C) 2007 Johannes E. Schindelin
5
 */
6
#include "builtin.h"
7
#include "config.h"
8
#include "gettext.h"
9
#include "hex.h"
10
#include "refs.h"
11
#include "refspec.h"
12
#include "object-file.h"
13
#include "object-store-ll.h"
14
#include "commit.h"
15
#include "object.h"
16
#include "tag.h"
17
#include "diff.h"
18
#include "diffcore.h"
19
#include "log-tree.h"
20
#include "revision.h"
21
#include "decorate.h"
22
#include "string-list.h"
23
#include "utf8.h"
24
#include "parse-options.h"
25
#include "quote.h"
26
#include "remote.h"
27
#include "blob.h"
28
#include "commit-slab.h"
29
30
static const char *fast_export_usage[] = {
31
  N_("git fast-export [<rev-list-opts>]"),
32
  NULL
33
};
34
35
static int progress;
36
static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
37
static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
38
static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
39
static int fake_missing_tagger;
40
static int use_done_feature;
41
static int no_data;
42
static int full_tree;
43
static int reference_excluded_commits;
44
static int show_original_ids;
45
static int mark_tags;
46
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
47
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
48
static struct refspec refspecs = REFSPEC_INIT_FETCH;
49
static int anonymize;
50
static struct hashmap anonymized_seeds;
51
static struct revision_sources revision_sources;
52
53
static int parse_opt_signed_tag_mode(const struct option *opt,
54
             const char *arg, int unset)
55
0
{
56
0
  enum signed_tag_mode *val = opt->value;
57
58
0
  if (unset || !strcmp(arg, "abort"))
59
0
    *val = SIGNED_TAG_ABORT;
60
0
  else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
61
0
    *val = VERBATIM;
62
0
  else if (!strcmp(arg, "warn"))
63
0
    *val = WARN;
64
0
  else if (!strcmp(arg, "warn-strip"))
65
0
    *val = WARN_STRIP;
66
0
  else if (!strcmp(arg, "strip"))
67
0
    *val = STRIP;
68
0
  else
69
0
    return error("Unknown signed-tags mode: %s", arg);
70
0
  return 0;
71
0
}
72
73
static int parse_opt_tag_of_filtered_mode(const struct option *opt,
74
            const char *arg, int unset)
75
0
{
76
0
  enum tag_of_filtered_mode *val = opt->value;
77
78
0
  if (unset || !strcmp(arg, "abort"))
79
0
    *val = TAG_FILTERING_ABORT;
80
0
  else if (!strcmp(arg, "drop"))
81
0
    *val = DROP;
82
0
  else if (!strcmp(arg, "rewrite"))
83
0
    *val = REWRITE;
84
0
  else
85
0
    return error("Unknown tag-of-filtered mode: %s", arg);
86
0
  return 0;
87
0
}
88
89
static int parse_opt_reencode_mode(const struct option *opt,
90
           const char *arg, int unset)
91
0
{
92
0
  enum reencode_mode *val = opt->value;
93
94
0
  if (unset) {
95
0
    *val = REENCODE_ABORT;
96
0
    return 0;
97
0
  }
98
99
0
  switch (git_parse_maybe_bool(arg)) {
100
0
  case 0:
101
0
    *val = REENCODE_NO;
102
0
    break;
103
0
  case 1:
104
0
    *val = REENCODE_YES;
105
0
    break;
106
0
  default:
107
0
    if (!strcasecmp(arg, "abort"))
108
0
      *val = REENCODE_ABORT;
109
0
    else
110
0
      return error("Unknown reencoding mode: %s", arg);
111
0
  }
112
113
0
  return 0;
114
0
}
115
116
static struct decoration idnums;
117
static uint32_t last_idnum;
118
struct anonymized_entry {
119
  struct hashmap_entry hash;
120
  char *anon;
121
  const char orig[FLEX_ARRAY];
122
};
123
124
struct anonymized_entry_key {
125
  struct hashmap_entry hash;
126
  const char *orig;
127
  size_t orig_len;
128
};
129
130
static int anonymized_entry_cmp(const void *cmp_data UNUSED,
131
        const struct hashmap_entry *eptr,
132
        const struct hashmap_entry *entry_or_key,
133
        const void *keydata)
134
0
{
135
0
  const struct anonymized_entry *a, *b;
136
137
0
  a = container_of(eptr, const struct anonymized_entry, hash);
138
0
  if (keydata) {
139
0
    const struct anonymized_entry_key *key = keydata;
140
0
    int equal = !strncmp(a->orig, key->orig, key->orig_len) &&
141
0
          !a->orig[key->orig_len];
142
0
    return !equal;
143
0
  }
144
145
0
  b = container_of(entry_or_key, const struct anonymized_entry, hash);
146
0
  return strcmp(a->orig, b->orig);
147
0
}
148
149
static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
150
                 unsigned hash,
151
                 const char *orig, size_t len,
152
                 char *anon)
153
0
{
154
0
  struct anonymized_entry *ret, *old;
155
156
0
  if (!map->cmpfn)
157
0
    hashmap_init(map, anonymized_entry_cmp, NULL, 0);
158
159
0
  FLEX_ALLOC_MEM(ret, orig, orig, len);
160
0
  hashmap_entry_init(&ret->hash, hash);
161
0
  ret->anon = anon;
162
0
  old = hashmap_put_entry(map, ret, hash);
163
164
0
  if (old) {
165
0
    free(old->anon);
166
0
    free(old);
167
0
  }
168
169
0
  return ret;
170
0
}
171
172
/*
173
 * Basically keep a cache of X->Y so that we can repeatedly replace
174
 * the same anonymized string with another. The actual generation
175
 * is farmed out to the generate function.
176
 */
177
static const char *anonymize_str(struct hashmap *map,
178
         char *(*generate)(void),
179
         const char *orig, size_t len)
180
0
{
181
0
  struct anonymized_entry_key key;
182
0
  struct anonymized_entry *ret;
183
184
0
  hashmap_entry_init(&key.hash, memhash(orig, len));
185
0
  key.orig = orig;
186
0
  key.orig_len = len;
187
188
  /* First check if it's a token the user configured manually... */
189
0
  ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
190
191
  /* ...otherwise check if we've already seen it in this context... */
192
0
  if (!ret)
193
0
    ret = hashmap_get_entry(map, &key, hash, &key);
194
195
  /* ...and finally generate a new mapping if necessary */
196
0
  if (!ret)
197
0
    ret = add_anonymized_entry(map, key.hash.hash,
198
0
             orig, len, generate());
199
200
0
  return ret->anon;
201
0
}
202
203
/*
204
 * We anonymize each component of a path individually,
205
 * so that paths a/b and a/c will share a common root.
206
 * The paths are cached via anonymize_mem so that repeated
207
 * lookups for "a" will yield the same value.
208
 */
209
static void anonymize_path(struct strbuf *out, const char *path,
210
         struct hashmap *map,
211
         char *(*generate)(void))
212
0
{
213
0
  while (*path) {
214
0
    const char *end_of_component = strchrnul(path, '/');
215
0
    size_t len = end_of_component - path;
216
0
    const char *c = anonymize_str(map, generate, path, len);
217
0
    strbuf_addstr(out, c);
218
0
    path = end_of_component;
219
0
    if (*path)
220
0
      strbuf_addch(out, *path++);
221
0
  }
222
0
}
223
224
static inline void *mark_to_ptr(uint32_t mark)
225
0
{
226
0
  return (void *)(uintptr_t)mark;
227
0
}
228
229
static inline uint32_t ptr_to_mark(void * mark)
230
0
{
231
0
  return (uint32_t)(uintptr_t)mark;
232
0
}
233
234
static inline void mark_object(struct object *object, uint32_t mark)
235
0
{
236
0
  add_decoration(&idnums, object, mark_to_ptr(mark));
237
0
}
238
239
static inline void mark_next_object(struct object *object)
240
0
{
241
0
  mark_object(object, ++last_idnum);
242
0
}
243
244
static int get_object_mark(struct object *object)
245
0
{
246
0
  void *decoration = lookup_decoration(&idnums, object);
247
0
  if (!decoration)
248
0
    return 0;
249
0
  return ptr_to_mark(decoration);
250
0
}
251
252
static struct commit *rewrite_commit(struct commit *p)
253
0
{
254
0
  for (;;) {
255
0
    if (p->parents && p->parents->next)
256
0
      break;
257
0
    if (p->object.flags & UNINTERESTING)
258
0
      break;
259
0
    if (!(p->object.flags & TREESAME))
260
0
      break;
261
0
    if (!p->parents)
262
0
      return NULL;
263
0
    p = p->parents->item;
264
0
  }
265
0
  return p;
266
0
}
267
268
static void show_progress(void)
269
0
{
270
0
  static int counter = 0;
271
0
  if (!progress)
272
0
    return;
273
0
  if ((++counter % progress) == 0)
274
0
    printf("progress %d objects\n", counter);
275
0
}
276
277
/*
278
 * Ideally we would want some transformation of the blob data here
279
 * that is unreversible, but would still be the same size and have
280
 * the same data relationship to other blobs (so that we get the same
281
 * delta and packing behavior as the original). But the first and last
282
 * requirements there are probably mutually exclusive, so let's take
283
 * the easy way out for now, and just generate arbitrary content.
284
 *
285
 * There's no need to cache this result with anonymize_mem, since
286
 * we already handle blob content caching with marks.
287
 */
288
static char *anonymize_blob(unsigned long *size)
289
0
{
290
0
  static int counter;
291
0
  struct strbuf out = STRBUF_INIT;
292
0
  strbuf_addf(&out, "anonymous blob %d", counter++);
293
0
  *size = out.len;
294
0
  return strbuf_detach(&out, NULL);
295
0
}
296
297
static void export_blob(const struct object_id *oid)
298
0
{
299
0
  unsigned long size;
300
0
  enum object_type type;
301
0
  char *buf;
302
0
  struct object *object;
303
0
  int eaten;
304
305
0
  if (no_data)
306
0
    return;
307
308
0
  if (is_null_oid(oid))
309
0
    return;
310
311
0
  object = lookup_object(the_repository, oid);
312
0
  if (object && object->flags & SHOWN)
313
0
    return;
314
315
0
  if (anonymize) {
316
0
    buf = anonymize_blob(&size);
317
0
    object = (struct object *)lookup_blob(the_repository, oid);
318
0
    eaten = 0;
319
0
  } else {
320
0
    buf = repo_read_object_file(the_repository, oid, &type, &size);
321
0
    if (!buf)
322
0
      die("could not read blob %s", oid_to_hex(oid));
323
0
    if (check_object_signature(the_repository, oid, buf, size,
324
0
             type) < 0)
325
0
      die("oid mismatch in blob %s", oid_to_hex(oid));
326
0
    object = parse_object_buffer(the_repository, oid, type,
327
0
               size, buf, &eaten);
328
0
  }
329
330
0
  if (!object)
331
0
    die("Could not read blob %s", oid_to_hex(oid));
332
333
0
  mark_next_object(object);
334
335
0
  printf("blob\nmark :%"PRIu32"\n", last_idnum);
336
0
  if (show_original_ids)
337
0
    printf("original-oid %s\n", oid_to_hex(oid));
338
0
  printf("data %"PRIuMAX"\n", (uintmax_t)size);
339
0
  if (size && fwrite(buf, size, 1, stdout) != 1)
340
0
    die_errno("could not write blob '%s'", oid_to_hex(oid));
341
0
  printf("\n");
342
343
0
  show_progress();
344
345
0
  object->flags |= SHOWN;
346
0
  if (!eaten)
347
0
    free(buf);
348
0
}
349
350
static int depth_first(const void *a_, const void *b_)
351
0
{
352
0
  const struct diff_filepair *a = *((const struct diff_filepair **)a_);
353
0
  const struct diff_filepair *b = *((const struct diff_filepair **)b_);
354
0
  const char *name_a, *name_b;
355
0
  int len_a, len_b, len;
356
0
  int cmp;
357
358
0
  name_a = a->one ? a->one->path : a->two->path;
359
0
  name_b = b->one ? b->one->path : b->two->path;
360
361
0
  len_a = strlen(name_a);
362
0
  len_b = strlen(name_b);
363
0
  len = (len_a < len_b) ? len_a : len_b;
364
365
  /* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
366
0
  cmp = memcmp(name_a, name_b, len);
367
0
  if (cmp)
368
0
    return cmp;
369
0
  cmp = len_b - len_a;
370
0
  if (cmp)
371
0
    return cmp;
372
  /*
373
   * Move 'R'ename entries last so that all references of the file
374
   * appear in the output before it is renamed (e.g., when a file
375
   * was copied and renamed in the same commit).
376
   */
377
0
  return (a->status == 'R') - (b->status == 'R');
378
0
}
379
380
static void print_path_1(const char *path)
381
0
{
382
0
  int need_quote = quote_c_style(path, NULL, NULL, 0);
383
0
  if (need_quote)
384
0
    quote_c_style(path, NULL, stdout, 0);
385
0
  else if (strchr(path, ' '))
386
0
    printf("\"%s\"", path);
387
0
  else
388
0
    printf("%s", path);
389
0
}
390
391
static char *anonymize_path_component(void)
392
0
{
393
0
  static int counter;
394
0
  struct strbuf out = STRBUF_INIT;
395
0
  strbuf_addf(&out, "path%d", counter++);
396
0
  return strbuf_detach(&out, NULL);
397
0
}
398
399
static void print_path(const char *path)
400
0
{
401
0
  if (!anonymize)
402
0
    print_path_1(path);
403
0
  else {
404
0
    static struct hashmap paths;
405
0
    static struct strbuf anon = STRBUF_INIT;
406
407
0
    anonymize_path(&anon, path, &paths, anonymize_path_component);
408
0
    print_path_1(anon.buf);
409
0
    strbuf_reset(&anon);
410
0
  }
411
0
}
412
413
static char *generate_fake_oid(void)
414
0
{
415
0
  static uint32_t counter = 1; /* avoid null oid */
416
0
  const unsigned hashsz = the_hash_algo->rawsz;
417
0
  struct object_id oid;
418
0
  char *hex = xmallocz(GIT_MAX_HEXSZ);
419
420
0
  oidclr(&oid);
421
0
  put_be32(oid.hash + hashsz - 4, counter++);
422
0
  return oid_to_hex_r(hex, &oid);
423
0
}
424
425
static const char *anonymize_oid(const char *oid_hex)
426
0
{
427
0
  static struct hashmap objs;
428
0
  size_t len = strlen(oid_hex);
429
0
  return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
430
0
}
431
432
static void show_filemodify(struct diff_queue_struct *q,
433
          struct diff_options *options UNUSED, void *data)
434
0
{
435
0
  int i;
436
0
  struct string_list *changed = data;
437
438
  /*
439
   * Handle files below a directory first, in case they are all deleted
440
   * and the directory changes to a file or symlink.
441
   */
442
0
  QSORT(q->queue, q->nr, depth_first);
443
444
0
  for (i = 0; i < q->nr; i++) {
445
0
    struct diff_filespec *ospec = q->queue[i]->one;
446
0
    struct diff_filespec *spec = q->queue[i]->two;
447
448
0
    switch (q->queue[i]->status) {
449
0
    case DIFF_STATUS_DELETED:
450
0
      printf("D ");
451
0
      print_path(spec->path);
452
0
      string_list_insert(changed, spec->path);
453
0
      putchar('\n');
454
0
      break;
455
456
0
    case DIFF_STATUS_COPIED:
457
0
    case DIFF_STATUS_RENAMED:
458
      /*
459
       * If a change in the file corresponding to ospec->path
460
       * has been observed, we cannot trust its contents
461
       * because the diff is calculated based on the prior
462
       * contents, not the current contents.  So, declare a
463
       * copy or rename only if there was no change observed.
464
       */
465
0
      if (!string_list_has_string(changed, ospec->path)) {
466
0
        printf("%c ", q->queue[i]->status);
467
0
        print_path(ospec->path);
468
0
        putchar(' ');
469
0
        print_path(spec->path);
470
0
        string_list_insert(changed, spec->path);
471
0
        putchar('\n');
472
473
0
        if (oideq(&ospec->oid, &spec->oid) &&
474
0
            ospec->mode == spec->mode)
475
0
          break;
476
0
      }
477
      /* fallthrough */
478
479
0
    case DIFF_STATUS_TYPE_CHANGED:
480
0
    case DIFF_STATUS_MODIFIED:
481
0
    case DIFF_STATUS_ADDED:
482
      /*
483
       * Links refer to objects in another repositories;
484
       * output the SHA-1 verbatim.
485
       */
486
0
      if (no_data || S_ISGITLINK(spec->mode))
487
0
        printf("M %06o %s ", spec->mode,
488
0
               anonymize ?
489
0
               anonymize_oid(oid_to_hex(&spec->oid)) :
490
0
               oid_to_hex(&spec->oid));
491
0
      else {
492
0
        struct object *object = lookup_object(the_repository,
493
0
                      &spec->oid);
494
0
        printf("M %06o :%d ", spec->mode,
495
0
               get_object_mark(object));
496
0
      }
497
0
      print_path(spec->path);
498
0
      string_list_insert(changed, spec->path);
499
0
      putchar('\n');
500
0
      break;
501
502
0
    default:
503
0
      die("Unexpected comparison status '%c' for %s, %s",
504
0
        q->queue[i]->status,
505
0
        ospec->path ? ospec->path : "none",
506
0
        spec->path ? spec->path : "none");
507
0
    }
508
0
  }
509
0
}
510
511
static const char *find_encoding(const char *begin, const char *end)
512
0
{
513
0
  const char *needle = "\nencoding ";
514
0
  char *bol, *eol;
515
516
0
  bol = memmem(begin, end ? end - begin : strlen(begin),
517
0
         needle, strlen(needle));
518
0
  if (!bol)
519
0
    return NULL;
520
0
  bol += strlen(needle);
521
0
  eol = strchrnul(bol, '\n');
522
0
  *eol = '\0';
523
0
  return bol;
524
0
}
525
526
static char *anonymize_ref_component(void)
527
0
{
528
0
  static int counter;
529
0
  struct strbuf out = STRBUF_INIT;
530
0
  strbuf_addf(&out, "ref%d", counter++);
531
0
  return strbuf_detach(&out, NULL);
532
0
}
533
534
static const char *anonymize_refname(const char *refname)
535
0
{
536
  /*
537
   * If any of these prefixes is found, we will leave it intact
538
   * so that tags remain tags and so forth.
539
   */
540
0
  static const char *prefixes[] = {
541
0
    "refs/heads/",
542
0
    "refs/tags/",
543
0
    "refs/remotes/",
544
0
    "refs/"
545
0
  };
546
0
  static struct hashmap refs;
547
0
  static struct strbuf anon = STRBUF_INIT;
548
0
  int i;
549
550
0
  strbuf_reset(&anon);
551
0
  for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
552
0
    if (skip_prefix(refname, prefixes[i], &refname)) {
553
0
      strbuf_addstr(&anon, prefixes[i]);
554
0
      break;
555
0
    }
556
0
  }
557
558
0
  anonymize_path(&anon, refname, &refs, anonymize_ref_component);
559
0
  return anon.buf;
560
0
}
561
562
/*
563
 * We do not even bother to cache commit messages, as they are unlikely
564
 * to be repeated verbatim, and it is not that interesting when they are.
565
 */
566
static char *anonymize_commit_message(void)
567
0
{
568
0
  static int counter;
569
0
  return xstrfmt("subject %d\n\nbody\n", counter++);
570
0
}
571
572
static char *anonymize_ident(void)
573
0
{
574
0
  static int counter;
575
0
  struct strbuf out = STRBUF_INIT;
576
0
  strbuf_addf(&out, "User %d <user%d@example.com>", counter, counter);
577
0
  counter++;
578
0
  return strbuf_detach(&out, NULL);
579
0
}
580
581
/*
582
 * Our strategy here is to anonymize the names and email addresses,
583
 * but keep timestamps intact, as they influence things like traversal
584
 * order (and by themselves should not be too revealing).
585
 */
586
static void anonymize_ident_line(const char **beg, const char **end)
587
0
{
588
0
  static struct hashmap idents;
589
0
  static struct strbuf buffers[] = { STRBUF_INIT, STRBUF_INIT };
590
0
  static unsigned which_buffer;
591
592
0
  struct strbuf *out;
593
0
  struct ident_split split;
594
0
  const char *end_of_header;
595
596
0
  out = &buffers[which_buffer++];
597
0
  which_buffer %= ARRAY_SIZE(buffers);
598
0
  strbuf_reset(out);
599
600
  /* skip "committer", "author", "tagger", etc */
601
0
  end_of_header = strchr(*beg, ' ');
602
0
  if (!end_of_header)
603
0
    BUG("malformed line fed to anonymize_ident_line: %.*s",
604
0
        (int)(*end - *beg), *beg);
605
0
  end_of_header++;
606
0
  strbuf_add(out, *beg, end_of_header - *beg);
607
608
0
  if (!split_ident_line(&split, end_of_header, *end - end_of_header) &&
609
0
      split.date_begin) {
610
0
    const char *ident;
611
0
    size_t len;
612
613
0
    len = split.mail_end - split.name_begin;
614
0
    ident = anonymize_str(&idents, anonymize_ident,
615
0
              split.name_begin, len);
616
0
    strbuf_addstr(out, ident);
617
0
    strbuf_addch(out, ' ');
618
0
    strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
619
0
  } else {
620
0
    strbuf_addstr(out, "Malformed Ident <malformed@example.com> 0 -0000");
621
0
  }
622
623
0
  *beg = out->buf;
624
0
  *end = out->buf + out->len;
625
0
}
626
627
static void handle_commit(struct commit *commit, struct rev_info *rev,
628
        struct string_list *paths_of_changed_objects)
629
0
{
630
0
  int saved_output_format = rev->diffopt.output_format;
631
0
  const char *commit_buffer;
632
0
  const char *author, *author_end, *committer, *committer_end;
633
0
  const char *encoding, *message;
634
0
  char *reencoded = NULL;
635
0
  struct commit_list *p;
636
0
  const char *refname;
637
0
  int i;
638
639
0
  rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
640
641
0
  parse_commit_or_die(commit);
642
0
  commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
643
0
  author = strstr(commit_buffer, "\nauthor ");
644
0
  if (!author)
645
0
    die("could not find author in commit %s",
646
0
        oid_to_hex(&commit->object.oid));
647
0
  author++;
648
0
  author_end = strchrnul(author, '\n');
649
0
  committer = strstr(author_end, "\ncommitter ");
650
0
  if (!committer)
651
0
    die("could not find committer in commit %s",
652
0
        oid_to_hex(&commit->object.oid));
653
0
  committer++;
654
0
  committer_end = strchrnul(committer, '\n');
655
0
  message = strstr(committer_end, "\n\n");
656
0
  encoding = find_encoding(committer_end, message);
657
0
  if (message)
658
0
    message += 2;
659
660
0
  if (commit->parents &&
661
0
      (get_object_mark(&commit->parents->item->object) != 0 ||
662
0
       reference_excluded_commits) &&
663
0
      !full_tree) {
664
0
    parse_commit_or_die(commit->parents->item);
665
0
    diff_tree_oid(get_commit_tree_oid(commit->parents->item),
666
0
            get_commit_tree_oid(commit), "", &rev->diffopt);
667
0
  }
668
0
  else
669
0
    diff_root_tree_oid(get_commit_tree_oid(commit),
670
0
           "", &rev->diffopt);
671
672
  /* Export the referenced blobs, and remember the marks. */
673
0
  for (i = 0; i < diff_queued_diff.nr; i++)
674
0
    if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
675
0
      export_blob(&diff_queued_diff.queue[i]->two->oid);
676
677
0
  refname = *revision_sources_at(&revision_sources, commit);
678
  /*
679
   * FIXME: string_list_remove() below for each ref is overall
680
   * O(N^2).  Compared to a history walk and diffing trees, this is
681
   * just lost in the noise in practice.  However, theoretically a
682
   * repo may have enough refs for this to become slow.
683
   */
684
0
  string_list_remove(&extra_refs, refname, 0);
685
0
  if (anonymize) {
686
0
    refname = anonymize_refname(refname);
687
0
    anonymize_ident_line(&committer, &committer_end);
688
0
    anonymize_ident_line(&author, &author_end);
689
0
  }
690
691
0
  mark_next_object(&commit->object);
692
0
  if (anonymize) {
693
0
    reencoded = anonymize_commit_message();
694
0
  } else if (encoding) {
695
0
    switch(reencode_mode) {
696
0
    case REENCODE_YES:
697
0
      reencoded = reencode_string(message, "UTF-8", encoding);
698
0
      break;
699
0
    case REENCODE_NO:
700
0
      break;
701
0
    case REENCODE_ABORT:
702
0
      die("Encountered commit-specific encoding %s in commit "
703
0
          "%s; use --reencode=[yes|no] to handle it",
704
0
          encoding, oid_to_hex(&commit->object.oid));
705
0
    }
706
0
  }
707
0
  if (!commit->parents)
708
0
    printf("reset %s\n", refname);
709
0
  printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
710
0
  if (show_original_ids)
711
0
    printf("original-oid %s\n", oid_to_hex(&commit->object.oid));
712
0
  printf("%.*s\n%.*s\n",
713
0
         (int)(author_end - author), author,
714
0
         (int)(committer_end - committer), committer);
715
0
  if (!reencoded && encoding)
716
0
    printf("encoding %s\n", encoding);
717
0
  printf("data %u\n%s",
718
0
         (unsigned)(reencoded
719
0
        ? strlen(reencoded) : message
720
0
        ? strlen(message) : 0),
721
0
         reencoded ? reencoded : message ? message : "");
722
0
  free(reencoded);
723
0
  repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
724
725
0
  for (i = 0, p = commit->parents; p; p = p->next) {
726
0
    struct object *obj = &p->item->object;
727
0
    int mark = get_object_mark(obj);
728
729
0
    if (!mark && !reference_excluded_commits)
730
0
      continue;
731
0
    if (i == 0)
732
0
      printf("from ");
733
0
    else
734
0
      printf("merge ");
735
0
    if (mark)
736
0
      printf(":%d\n", mark);
737
0
    else
738
0
      printf("%s\n",
739
0
             anonymize ?
740
0
             anonymize_oid(oid_to_hex(&obj->oid)) :
741
0
             oid_to_hex(&obj->oid));
742
0
    i++;
743
0
  }
744
745
0
  if (full_tree)
746
0
    printf("deleteall\n");
747
0
  log_tree_diff_flush(rev);
748
0
  string_list_clear(paths_of_changed_objects, 0);
749
0
  rev->diffopt.output_format = saved_output_format;
750
751
0
  printf("\n");
752
753
0
  show_progress();
754
0
}
755
756
static char *anonymize_tag(void)
757
0
{
758
0
  static int counter;
759
0
  struct strbuf out = STRBUF_INIT;
760
0
  strbuf_addf(&out, "tag message %d", counter++);
761
0
  return strbuf_detach(&out, NULL);
762
0
}
763
764
765
static void handle_tag(const char *name, struct tag *tag)
766
0
{
767
0
  unsigned long size;
768
0
  enum object_type type;
769
0
  char *buf;
770
0
  const char *tagger, *tagger_end, *message;
771
0
  size_t message_size = 0;
772
0
  struct object *tagged;
773
0
  int tagged_mark;
774
0
  struct commit *p;
775
776
  /* Trees have no identifier in fast-export output, thus we have no way
777
   * to output tags of trees, tags of tags of trees, etc.  Simply omit
778
   * such tags.
779
   */
780
0
  tagged = tag->tagged;
781
0
  while (tagged->type == OBJ_TAG) {
782
0
    tagged = ((struct tag *)tagged)->tagged;
783
0
  }
784
0
  if (tagged->type == OBJ_TREE) {
785
0
    warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
786
0
      oid_to_hex(&tag->object.oid));
787
0
    return;
788
0
  }
789
790
0
  buf = repo_read_object_file(the_repository, &tag->object.oid, &type,
791
0
            &size);
792
0
  if (!buf)
793
0
    die("could not read tag %s", oid_to_hex(&tag->object.oid));
794
0
  message = memmem(buf, size, "\n\n", 2);
795
0
  if (message) {
796
0
    message += 2;
797
0
    message_size = strlen(message);
798
0
  }
799
0
  tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
800
0
  if (!tagger) {
801
0
    if (fake_missing_tagger)
802
0
      tagger = "tagger Unspecified Tagger "
803
0
        "<unspecified-tagger> 0 +0000";
804
0
    else
805
0
      tagger = "";
806
0
    tagger_end = tagger + strlen(tagger);
807
0
  } else {
808
0
    tagger++;
809
0
    tagger_end = strchrnul(tagger, '\n');
810
0
    if (anonymize)
811
0
      anonymize_ident_line(&tagger, &tagger_end);
812
0
  }
813
814
0
  if (anonymize) {
815
0
    name = anonymize_refname(name);
816
0
    if (message) {
817
0
      static struct hashmap tags;
818
0
      message = anonymize_str(&tags, anonymize_tag,
819
0
            message, message_size);
820
0
      message_size = strlen(message);
821
0
    }
822
0
  }
823
824
  /* handle signed tags */
825
0
  if (message) {
826
0
    const char *signature = strstr(message,
827
0
                 "\n-----BEGIN PGP SIGNATURE-----\n");
828
0
    if (signature)
829
0
      switch(signed_tag_mode) {
830
0
      case SIGNED_TAG_ABORT:
831
0
        die("encountered signed tag %s; use "
832
0
            "--signed-tags=<mode> to handle it",
833
0
            oid_to_hex(&tag->object.oid));
834
0
      case WARN:
835
0
        warning("exporting signed tag %s",
836
0
          oid_to_hex(&tag->object.oid));
837
        /* fallthru */
838
0
      case VERBATIM:
839
0
        break;
840
0
      case WARN_STRIP:
841
0
        warning("stripping signature from tag %s",
842
0
          oid_to_hex(&tag->object.oid));
843
        /* fallthru */
844
0
      case STRIP:
845
0
        message_size = signature + 1 - message;
846
0
        break;
847
0
      }
848
0
  }
849
850
  /* handle tag->tagged having been filtered out due to paths specified */
851
0
  tagged = tag->tagged;
852
0
  tagged_mark = get_object_mark(tagged);
853
0
  if (!tagged_mark) {
854
0
    switch(tag_of_filtered_mode) {
855
0
    case TAG_FILTERING_ABORT:
856
0
      die("tag %s tags unexported object; use "
857
0
          "--tag-of-filtered-object=<mode> to handle it",
858
0
          oid_to_hex(&tag->object.oid));
859
0
    case DROP:
860
      /* Ignore this tag altogether */
861
0
      free(buf);
862
0
      return;
863
0
    case REWRITE:
864
0
      if (tagged->type == OBJ_TAG && !mark_tags) {
865
0
        die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
866
0
      } else if (tagged->type == OBJ_COMMIT) {
867
0
        p = rewrite_commit((struct commit *)tagged);
868
0
        if (!p) {
869
0
          printf("reset %s\nfrom %s\n\n",
870
0
                 name, oid_to_hex(null_oid()));
871
0
          free(buf);
872
0
          return;
873
0
        }
874
0
        tagged_mark = get_object_mark(&p->object);
875
0
      } else {
876
        /* tagged->type is either OBJ_BLOB or OBJ_TAG */
877
0
        tagged_mark = get_object_mark(tagged);
878
0
      }
879
0
    }
880
0
  }
881
882
0
  if (tagged->type == OBJ_TAG) {
883
0
    printf("reset %s\nfrom %s\n\n",
884
0
           name, oid_to_hex(null_oid()));
885
0
  }
886
0
  skip_prefix(name, "refs/tags/", &name);
887
0
  printf("tag %s\n", name);
888
0
  if (mark_tags) {
889
0
    mark_next_object(&tag->object);
890
0
    printf("mark :%"PRIu32"\n", last_idnum);
891
0
  }
892
0
  if (tagged_mark)
893
0
    printf("from :%d\n", tagged_mark);
894
0
  else
895
0
    printf("from %s\n", oid_to_hex(&tagged->oid));
896
897
0
  if (show_original_ids)
898
0
    printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
899
0
  printf("%.*s%sdata %d\n%.*s\n",
900
0
         (int)(tagger_end - tagger), tagger,
901
0
         tagger == tagger_end ? "" : "\n",
902
0
         (int)message_size, (int)message_size, message ? message : "");
903
0
  free(buf);
904
0
}
905
906
static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
907
0
{
908
0
  switch (e->item->type) {
909
0
  case OBJ_COMMIT:
910
0
    return (struct commit *)e->item;
911
0
  case OBJ_TAG: {
912
0
    struct tag *tag = (struct tag *)e->item;
913
914
    /* handle nested tags */
915
0
    while (tag && tag->object.type == OBJ_TAG) {
916
0
      parse_object(the_repository, &tag->object.oid);
917
0
      string_list_append(&tag_refs, full_name)->util = tag;
918
0
      tag = (struct tag *)tag->tagged;
919
0
    }
920
0
    if (!tag)
921
0
      die("Tag %s points nowhere?", e->name);
922
0
    return (struct commit *)tag;
923
0
  }
924
0
  default:
925
0
    return NULL;
926
0
  }
927
0
}
928
929
static void get_tags_and_duplicates(struct rev_cmdline_info *info)
930
0
{
931
0
  int i;
932
933
0
  for (i = 0; i < info->nr; i++) {
934
0
    struct rev_cmdline_entry *e = info->rev + i;
935
0
    struct object_id oid;
936
0
    struct commit *commit;
937
0
    char *full_name;
938
939
0
    if (e->flags & UNINTERESTING)
940
0
      continue;
941
942
0
    if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
943
0
          &oid, &full_name, 0) != 1)
944
0
      continue;
945
946
0
    if (refspecs.nr) {
947
0
      char *private;
948
0
      private = apply_refspecs(&refspecs, full_name);
949
0
      if (private) {
950
0
        free(full_name);
951
0
        full_name = private;
952
0
      }
953
0
    }
954
955
0
    commit = get_commit(e, full_name);
956
0
    if (!commit) {
957
0
      warning("%s: Unexpected object of type %s, skipping.",
958
0
        e->name,
959
0
        type_name(e->item->type));
960
0
      continue;
961
0
    }
962
963
0
    switch(commit->object.type) {
964
0
    case OBJ_COMMIT:
965
0
      break;
966
0
    case OBJ_BLOB:
967
0
      export_blob(&commit->object.oid);
968
0
      continue;
969
0
    default: /* OBJ_TAG (nested tags) is already handled */
970
0
      warning("Tag points to object of unexpected type %s, skipping.",
971
0
        type_name(commit->object.type));
972
0
      continue;
973
0
    }
974
975
    /*
976
     * Make sure this ref gets properly updated eventually, whether
977
     * through a commit or manually at the end.
978
     */
979
0
    if (e->item->type != OBJ_TAG)
980
0
      string_list_append(&extra_refs, full_name)->util = commit;
981
982
0
    if (!*revision_sources_at(&revision_sources, commit))
983
0
      *revision_sources_at(&revision_sources, commit) = full_name;
984
0
  }
985
986
0
  string_list_sort(&extra_refs);
987
0
  string_list_remove_duplicates(&extra_refs, 0);
988
0
}
989
990
static void handle_tags_and_duplicates(struct string_list *extras)
991
0
{
992
0
  struct commit *commit;
993
0
  int i;
994
995
0
  for (i = extras->nr - 1; i >= 0; i--) {
996
0
    const char *name = extras->items[i].string;
997
0
    struct object *object = extras->items[i].util;
998
0
    int mark;
999
1000
0
    switch (object->type) {
1001
0
    case OBJ_TAG:
1002
0
      handle_tag(name, (struct tag *)object);
1003
0
      break;
1004
0
    case OBJ_COMMIT:
1005
0
      if (anonymize)
1006
0
        name = anonymize_refname(name);
1007
      /* create refs pointing to already seen commits */
1008
0
      commit = rewrite_commit((struct commit *)object);
1009
0
      if (!commit) {
1010
        /*
1011
         * Neither this object nor any of its
1012
         * ancestors touch any relevant paths, so
1013
         * it has been filtered to nothing.  Delete
1014
         * it.
1015
         */
1016
0
        printf("reset %s\nfrom %s\n\n",
1017
0
               name, oid_to_hex(null_oid()));
1018
0
        continue;
1019
0
      }
1020
1021
0
      mark = get_object_mark(&commit->object);
1022
0
      if (!mark) {
1023
        /*
1024
         * Getting here means we have a commit which
1025
         * was excluded by a negative refspec (e.g.
1026
         * fast-export ^HEAD HEAD).  If we are
1027
         * referencing excluded commits, set the ref
1028
         * to the exact commit.  Otherwise, the user
1029
         * wants the branch exported but every commit
1030
         * in its history to be deleted, which basically
1031
         * just means deletion of the ref.
1032
         */
1033
0
        if (!reference_excluded_commits) {
1034
          /* delete the ref */
1035
0
          printf("reset %s\nfrom %s\n\n",
1036
0
                 name, oid_to_hex(null_oid()));
1037
0
          continue;
1038
0
        }
1039
        /* set ref to commit using oid, not mark */
1040
0
        printf("reset %s\nfrom %s\n\n", name,
1041
0
               oid_to_hex(&commit->object.oid));
1042
0
        continue;
1043
0
      }
1044
1045
0
      printf("reset %s\nfrom :%d\n\n", name, mark
1046
0
             );
1047
0
      show_progress();
1048
0
      break;
1049
0
    }
1050
0
  }
1051
0
}
1052
1053
static void export_marks(char *file)
1054
0
{
1055
0
  unsigned int i;
1056
0
  uint32_t mark;
1057
0
  struct decoration_entry *deco = idnums.entries;
1058
0
  FILE *f;
1059
0
  int e = 0;
1060
1061
0
  f = fopen_for_writing(file);
1062
0
  if (!f)
1063
0
    die_errno("Unable to open marks file %s for writing.", file);
1064
1065
0
  for (i = 0; i < idnums.size; i++) {
1066
0
    if (deco->base && deco->base->type == 1) {
1067
0
      mark = ptr_to_mark(deco->decoration);
1068
0
      if (fprintf(f, ":%"PRIu32" %s\n", mark,
1069
0
        oid_to_hex(&deco->base->oid)) < 0) {
1070
0
          e = 1;
1071
0
          break;
1072
0
      }
1073
0
    }
1074
0
    deco++;
1075
0
  }
1076
1077
0
  e |= ferror(f);
1078
0
  e |= fclose(f);
1079
0
  if (e)
1080
0
    error("Unable to write marks file %s.", file);
1081
0
}
1082
1083
static void import_marks(char *input_file, int check_exists)
1084
0
{
1085
0
  char line[512];
1086
0
  FILE *f;
1087
0
  struct stat sb;
1088
1089
0
  if (check_exists && stat(input_file, &sb))
1090
0
    return;
1091
1092
0
  f = xfopen(input_file, "r");
1093
0
  while (fgets(line, sizeof(line), f)) {
1094
0
    uint32_t mark;
1095
0
    char *line_end, *mark_end;
1096
0
    struct object_id oid;
1097
0
    struct object *object;
1098
0
    struct commit *commit;
1099
0
    enum object_type type;
1100
1101
0
    line_end = strchr(line, '\n');
1102
0
    if (line[0] != ':' || !line_end)
1103
0
      die("corrupt mark line: %s", line);
1104
0
    *line_end = '\0';
1105
1106
0
    mark = strtoumax(line + 1, &mark_end, 10);
1107
0
    if (!mark || mark_end == line + 1
1108
0
      || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
1109
0
      die("corrupt mark line: %s", line);
1110
1111
0
    if (last_idnum < mark)
1112
0
      last_idnum = mark;
1113
1114
0
    type = oid_object_info(the_repository, &oid, NULL);
1115
0
    if (type < 0)
1116
0
      die("object not found: %s", oid_to_hex(&oid));
1117
1118
0
    if (type != OBJ_COMMIT)
1119
      /* only commits */
1120
0
      continue;
1121
1122
0
    commit = lookup_commit(the_repository, &oid);
1123
0
    if (!commit)
1124
0
      die("not a commit? can't happen: %s", oid_to_hex(&oid));
1125
1126
0
    object = &commit->object;
1127
1128
0
    if (object->flags & SHOWN)
1129
0
      error("Object %s already has a mark", oid_to_hex(&oid));
1130
1131
0
    mark_object(object, mark);
1132
1133
0
    object->flags |= SHOWN;
1134
0
  }
1135
0
  fclose(f);
1136
0
}
1137
1138
static void handle_deletes(void)
1139
0
{
1140
0
  int i;
1141
0
  for (i = 0; i < refspecs.nr; i++) {
1142
0
    struct refspec_item *refspec = &refspecs.items[i];
1143
0
    if (*refspec->src)
1144
0
      continue;
1145
1146
0
    printf("reset %s\nfrom %s\n\n",
1147
0
        refspec->dst, oid_to_hex(null_oid()));
1148
0
  }
1149
0
}
1150
1151
static int parse_opt_anonymize_map(const struct option *opt,
1152
           const char *arg, int unset)
1153
0
{
1154
0
  struct hashmap *map = opt->value;
1155
0
  const char *delim, *value;
1156
0
  size_t keylen;
1157
1158
0
  BUG_ON_OPT_NEG(unset);
1159
1160
0
  delim = strchr(arg, ':');
1161
0
  if (delim) {
1162
0
    keylen = delim - arg;
1163
0
    value = delim + 1;
1164
0
  } else {
1165
0
    keylen = strlen(arg);
1166
0
    value = arg;
1167
0
  }
1168
1169
0
  if (!keylen || !*value)
1170
0
    return error(_("--anonymize-map token cannot be empty"));
1171
1172
0
  add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
1173
0
           xstrdup(value));
1174
1175
0
  return 0;
1176
0
}
1177
1178
int cmd_fast_export(int argc, const char **argv, const char *prefix)
1179
0
{
1180
0
  struct rev_info revs;
1181
0
  struct commit *commit;
1182
0
  char *export_filename = NULL,
1183
0
       *import_filename = NULL,
1184
0
       *import_filename_if_exists = NULL;
1185
0
  uint32_t lastimportid;
1186
0
  struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
1187
0
  struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
1188
0
  struct option options[] = {
1189
0
    OPT_INTEGER(0, "progress", &progress,
1190
0
          N_("show progress after <n> objects")),
1191
0
    OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"),
1192
0
           N_("select handling of signed tags"),
1193
0
           parse_opt_signed_tag_mode),
1194
0
    OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
1195
0
           N_("select handling of tags that tag filtered objects"),
1196
0
           parse_opt_tag_of_filtered_mode),
1197
0
    OPT_CALLBACK(0, "reencode", &reencode_mode, N_("mode"),
1198
0
           N_("select handling of commit messages in an alternate encoding"),
1199
0
           parse_opt_reencode_mode),
1200
0
    OPT_STRING(0, "export-marks", &export_filename, N_("file"),
1201
0
           N_("dump marks to this file")),
1202
0
    OPT_STRING(0, "import-marks", &import_filename, N_("file"),
1203
0
           N_("import marks from this file")),
1204
0
    OPT_STRING(0, "import-marks-if-exists",
1205
0
           &import_filename_if_exists,
1206
0
           N_("file"),
1207
0
           N_("import marks from this file if it exists")),
1208
0
    OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
1209
0
       N_("fake a tagger when tags lack one")),
1210
0
    OPT_BOOL(0, "full-tree", &full_tree,
1211
0
       N_("output full tree for each commit")),
1212
0
    OPT_BOOL(0, "use-done-feature", &use_done_feature,
1213
0
           N_("use the done feature to terminate the stream")),
1214
0
    OPT_BOOL(0, "no-data", &no_data, N_("skip output of blob data")),
1215
0
    OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
1216
0
           N_("apply refspec to exported refs")),
1217
0
    OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
1218
0
    OPT_CALLBACK_F(0, "anonymize-map", &anonymized_seeds, N_("from:to"),
1219
0
             N_("convert <from> to <to> in anonymized output"),
1220
0
             PARSE_OPT_NONEG, parse_opt_anonymize_map),
1221
0
    OPT_BOOL(0, "reference-excluded-parents",
1222
0
       &reference_excluded_commits, N_("reference parents which are not in fast-export stream by object id")),
1223
0
    OPT_BOOL(0, "show-original-ids", &show_original_ids,
1224
0
          N_("show original object ids of blobs/commits")),
1225
0
    OPT_BOOL(0, "mark-tags", &mark_tags,
1226
0
          N_("label tags with mark ids")),
1227
1228
0
    OPT_END()
1229
0
  };
1230
1231
0
  if (argc == 1)
1232
0
    usage_with_options (fast_export_usage, options);
1233
1234
  /* we handle encodings */
1235
0
  git_config(git_default_config, NULL);
1236
1237
0
  repo_init_revisions(the_repository, &revs, prefix);
1238
0
  init_revision_sources(&revision_sources);
1239
0
  revs.topo_order = 1;
1240
0
  revs.sources = &revision_sources;
1241
0
  revs.rewrite_parents = 1;
1242
0
  argc = parse_options(argc, argv, prefix, options, fast_export_usage,
1243
0
      PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
1244
0
  argc = setup_revisions(argc, argv, &revs, NULL);
1245
0
  if (argc > 1)
1246
0
    usage_with_options (fast_export_usage, options);
1247
1248
0
  if (anonymized_seeds.cmpfn && !anonymize)
1249
0
    die(_("the option '%s' requires '%s'"), "--anonymize-map", "--anonymize");
1250
1251
0
  if (refspecs_list.nr) {
1252
0
    int i;
1253
1254
0
    for (i = 0; i < refspecs_list.nr; i++)
1255
0
      refspec_append(&refspecs, refspecs_list.items[i].string);
1256
1257
0
    string_list_clear(&refspecs_list, 1);
1258
0
  }
1259
1260
0
  if (use_done_feature)
1261
0
    printf("feature done\n");
1262
1263
0
  if (import_filename && import_filename_if_exists)
1264
0
    die(_("options '%s' and '%s' cannot be used together"), "--import-marks", "--import-marks-if-exists");
1265
0
  if (import_filename)
1266
0
    import_marks(import_filename, 0);
1267
0
  else if (import_filename_if_exists)
1268
0
    import_marks(import_filename_if_exists, 1);
1269
0
  lastimportid = last_idnum;
1270
1271
0
  if (import_filename && revs.prune_data.nr)
1272
0
    full_tree = 1;
1273
1274
0
  get_tags_and_duplicates(&revs.cmdline);
1275
1276
0
  if (prepare_revision_walk(&revs))
1277
0
    die("revision walk setup failed");
1278
1279
0
  revs.reverse = 1;
1280
0
  revs.diffopt.format_callback = show_filemodify;
1281
0
  revs.diffopt.format_callback_data = &paths_of_changed_objects;
1282
0
  revs.diffopt.flags.recursive = 1;
1283
0
  revs.diffopt.no_free = 1;
1284
0
  while ((commit = get_revision(&revs)))
1285
0
    handle_commit(commit, &revs, &paths_of_changed_objects);
1286
1287
0
  handle_tags_and_duplicates(&extra_refs);
1288
0
  handle_tags_and_duplicates(&tag_refs);
1289
0
  handle_deletes();
1290
1291
0
  if (export_filename && lastimportid != last_idnum)
1292
0
    export_marks(export_filename);
1293
1294
0
  if (use_done_feature)
1295
0
    printf("done\n");
1296
1297
0
  refspec_clear(&refspecs);
1298
0
  release_revisions(&revs);
1299
1300
0
  return 0;
1301
0
}