Coverage Report

Created: 2024-09-08 06:23

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