/*
* "git fast-export" builtin command
*
* Copyright (C) 2007 Johannes E. Schindelin
*/
#include "builtin.h"
#include "cache.h"
#include "config.h"
#include "refs.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
#include "diff.h"
#include "diffcore.h"
#include "log-tree.h"
#include "revision.h"
#include "decorate.h"
#include "string-list.h"
#include "utf8.h"
#include "parse-options.h"
#include "quote.h"
#include "remote.h"
#include "blob.h"
static const char *fast_export_usage[] = {
N_("git fast-export [rev-list-opts]"),
NULL
};
static int progress;
static enum { ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = ABORT;
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
static int fake_missing_tagger;
static int use_done_feature;
static int no_data;
static int full_tree;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
static struct refspec *refspecs;
static int refspecs_nr;
static int anonymize;
static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
{
if (unset || !strcmp(arg, "abort"))
signed_tag_mode = ABORT;
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
signed_tag_mode = VERBATIM;
else if (!strcmp(arg, "warn"))
signed_tag_mode = WARN;
else if (!strcmp(arg, "warn-strip"))
signed_tag_mode = WARN_STRIP;
else if (!strcmp(arg, "strip"))
signed_tag_mode = STRIP;
else
return error("Unknown signed-tags mode: %s", arg);
return 0;
}
static int parse_opt_tag_of_filtered_mode(const struct option *opt,
const char *arg, int unset)
{
if (unset || !strcmp(arg, "abort"))
tag_of_filtered_mode = ERROR;
else if (!strcmp(arg, "drop"))
tag_of_filtered_mode = DROP;
else if (!strcmp(arg, "rewrite"))
tag_of_filtered_mode = REWRITE;
else
return error("Unknown tag-of-filtered mode: %s", arg);
return 0;
}
static struct decoration idnums;
static uint32_t last_idnum;
static int has_unshown_parent(struct commit *commit)
{
struct commit_list *parent;
for (parent = commit->parents; parent; parent = parent->next)
if (!(parent->item->object.flags & SHOWN) &&
!(parent->item->object.flags & UNINTERESTING))
return 1;
return 0;
}
struct anonymized_entry {
struct hashmap_entry hash;
const char *orig;
size_t orig_len;
const char *anon;
size_t anon_len;
};
static int anonymized_entry_cmp(const void *unused_cmp_data,
const void *va, const void *vb,
const void *unused_keydata)
{
const struct anonymized_entry *a = va, *b = vb;
return a->orig_len != b->orig_len ||
memcmp(a->orig, b->orig, a->orig_len);
}
/*
* Basically keep a cache of X->Y so that we can repeatedly replace
* the same anonymized string with another. The actual generation
* is farmed out to the generate function.
*/
static const void *anonymize_mem(struct hashmap *map,
void *(*generate)(const void *, size_t *),
const void *orig, size_t *len)
{
struct anonymized_entry key, *ret;
if (!map->cmpfn)
hashmap_init(map, anonymized_entry_cmp, NULL, 0);
hashmap_entry_init(&key, memhash(orig, *len));
key.orig = orig;
key.orig_len = *len;
ret = hashmap_get(map, &key, NULL);
if (!ret) {
ret = xmalloc(sizeof(*ret));
hashmap_entry_init(&ret->hash, key.hash.hash);
ret->orig = xstrdup(orig);
ret->orig_len = *len;
ret->anon = generate(orig, len);
ret->anon_len = *len;
hashmap_put(map, ret);
}
*len = ret->anon_len;
return ret->anon;
}
/*
* We anonymize each component of a path individually,
* so that paths a/b and a/c will share a common root.
* The paths are cached via anonymize_mem so that repeated
* lookups for "a" will yield the same value.
*/
static void anonymize_path(struct strbuf *out, const char *path,
struct hashmap *map,
void *(*generate)(const void *, size_t *))
{
while (*path) {
const char *end_of_component = strchrnul(path, '/');
size_t len = end_of_component - path;
const char *c = anonymize_mem(map, generate, path, &len);
strbuf_add(out, c, len);
path = end_of_component;
if (*path)
strbuf_addch(out, *path++);
}
}
/* Since intptr_t is C99, we do not use it here */
static inline uint32_t *mark_to_ptr(uint32_t mark)
{
return ((uint32_t *)NULL) + mark;
}
static inline uint32_t ptr_to_mark(void
|