printf("D %s\n", spec->path);
else {
struct object *object = lookup_object(spec->sha1);
- printf("M 0%06o :%d %s\n", spec->mode,
+ printf("M %06o :%d %s\n", spec->mode,
get_object_mark(object), spec->path);
}
}
get_tags_and_duplicates(&revs.pending, &extra_refs);
- prepare_revision_walk(&revs);
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
while ((commit = get_revision(&revs))) {
die("Could not make %s writable by group\n", dir);
}
- static int copy_file(const char *dst, const char *src, int mode)
- {
- int fdi, fdo, status;
-
- mode = (mode & 0111) ? 0777 : 0666;
- if ((fdi = open(src, O_RDONLY)) < 0)
- return fdi;
- if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
- close(fdi);
- return fdo;
- }
- status = copy_fd(fdi, fdo);
- if (close(fdo) != 0)
- return error("%s: write error: %s", dst, strerror(errno));
-
- if (!status && adjust_shared_perm(dst))
- return -1;
-
- return status;
- }
-
static void copy_templates_1(char *path, int baselen,
char *template, int template_baselen,
DIR *dir)
*/
template_dir = DEFAULT_GIT_TEMPLATE_DIR;
if (!is_absolute_path(template_dir)) {
- const char *exec_path = git_exec_path();
- template_dir = prefix_path(exec_path, strlen(exec_path),
- template_dir);
+ struct strbuf d = STRBUF_INIT;
+ strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir);
+ template_dir = strbuf_detach(&d, NULL);
}
}
strcpy(template_path, template_dir);
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e2, e3) &&
- S_ISREG(ntohl(e2->ce_mode)) &&
- S_ISREG(ntohl(e3->ce_mode))) {
+ S_ISREG(e2->ce_mode) &&
+ S_ISREG(e3->ce_mode)) {
path_list_insert((const char *)e2->name, conflict);
i++; /* skip over both #2 and #3 */
}
return 0;
}
- static int copy_file(const char *src, const char *dest)
- {
- FILE *in, *out;
- char buffer[32768];
- int count;
-
- if (!(in = fopen(src, "r")))
- return error("Could not open %s", src);
- if (!(out = fopen(dest, "w")))
- return error("Could not open %s", dest);
- while ((count = fread(buffer, 1, sizeof(buffer), in)))
- fwrite(buffer, 1, count, out);
- fclose(in);
- fclose(out);
- return 0;
- }
-
static int do_plain_rerere(struct path_list *rr, int fd)
{
struct path_list conflict = { NULL, 0, 0, 1 };
continue;
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
- copy_file(path, rr_path(name, "postimage"));
+ copy_file(rr_path(name, "postimage"), path, 0666);
tail_optimization:
if (i < rr->nr - 1)
memmove(rr->items + i,
#include "git-compat-util.h"
#include "strbuf.h"
+#include "hash.h"
#include SHA1_HEADER
#include <zlib.h>
* We save the fields in big-endian order to allow using the
* index file over NFS transparently.
*/
+struct ondisk_cache_entry {
+ struct cache_time ctime;
+ struct cache_time mtime;
+ unsigned int dev;
+ unsigned int ino;
+ unsigned int mode;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int size;
+ unsigned char sha1[20];
+ unsigned short flags;
+ char name[FLEX_ARRAY]; /* more */
+};
+
struct cache_entry {
- struct cache_time ce_ctime;
- struct cache_time ce_mtime;
+ unsigned int ce_ctime;
+ unsigned int ce_mtime;
unsigned int ce_dev;
unsigned int ce_ino;
unsigned int ce_mode;
unsigned int ce_uid;
unsigned int ce_gid;
unsigned int ce_size;
+ unsigned int ce_flags;
unsigned char sha1[20];
- unsigned short ce_flags;
+ struct cache_entry *next;
char name[FLEX_ARRAY]; /* more */
};
#define CE_NAMEMASK (0x0fff)
#define CE_STAGEMASK (0x3000)
-#define CE_UPDATE (0x4000)
#define CE_VALID (0x8000)
#define CE_STAGESHIFT 12
-#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
-#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
+/* In-memory only */
+#define CE_UPDATE (0x10000)
+#define CE_REMOVE (0x20000)
+#define CE_UPTODATE (0x40000)
+
+#define CE_HASHED (0x100000)
+#define CE_UNHASHED (0x200000)
+
+/*
+ * Copy the sha1 and stat state of a cache entry from one to
+ * another. But we never change the name, or the hash state!
+ */
+#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
+static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+{
+ unsigned int state = dst->ce_flags & CE_STATE_MASK;
+
+ /* Don't copy hash chain and name */
+ memcpy(dst, src, offsetof(struct cache_entry, next));
+
+ /* Restore the hash state */
+ dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
+}
+
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static inline void remove_index_entry(struct cache_entry *ce)
+{
+ ce->ce_flags |= CE_UNHASHED;
+}
+
+static inline unsigned create_ce_flags(size_t len, unsigned stage)
+{
+ if (len >= CE_NAMEMASK)
+ len = CE_NAMEMASK;
+ return (len | (stage << CE_STAGESHIFT));
+}
+
+static inline size_t ce_namelen(const struct cache_entry *ce)
+{
+ size_t len = ce->ce_flags & CE_NAMEMASK;
+ if (len < CE_NAMEMASK)
+ return len;
+ return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
+}
+
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
+#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
static inline unsigned int create_ce_mode(unsigned int mode)
{
if (S_ISLNK(mode))
- return htonl(S_IFLNK);
+ return S_IFLNK;
if (S_ISDIR(mode) || S_ISGITLINK(mode))
- return htonl(S_IFGITLINK);
- return htonl(S_IFREG | ce_permissions(mode));
+ return S_IFGITLINK;
+ return S_IFREG | ce_permissions(mode);
}
static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
{
extern int trust_executable_bit, has_symlinks;
if (!has_symlinks && S_ISREG(mode) &&
- ce && S_ISLNK(ntohl(ce->ce_mode)))
+ ce && S_ISLNK(ce->ce_mode))
return ce->ce_mode;
if (!trust_executable_bit && S_ISREG(mode)) {
- if (ce && S_ISREG(ntohl(ce->ce_mode)))
+ if (ce && S_ISREG(ce->ce_mode))
return ce->ce_mode;
return create_ce_mode(0666);
}
return create_ce_mode(mode);
}
+static inline int ce_to_dtype(const struct cache_entry *ce)
+{
+ unsigned ce_mode = ntohl(ce->ce_mode);
+ if (S_ISREG(ce_mode))
+ return DT_REG;
+ else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+ return DT_DIR;
+ else if (S_ISLNK(ce_mode))
+ return DT_LNK;
+ else
+ return DT_UNKNOWN;
+}
#define canon_mode(mode) \
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
+#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
struct index_state {
struct cache_entry **cache;
unsigned int cache_nr, cache_alloc, cache_changed;
struct cache_tree *cache_tree;
time_t timestamp;
- void *mmap;
- size_t mmap_size;
+ void *alloc;
+ unsigned name_hash_initialized : 1;
+ struct hash_table name_hash;
};
extern struct index_state the_index;
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
+#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
#endif
enum object_type {
extern int write_index(struct index_state *, int newfd);
extern int discard_index(struct index_state *);
extern int verify_path(const char *path);
+extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
extern int index_name_pos(struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
extern size_t delta_base_cache_limit;
extern int auto_crlf;
+enum safe_crlf {
+ SAFE_CRLF_FALSE = 0,
+ SAFE_CRLF_FAIL = 1,
+ SAFE_CRLF_WARN = 2,
+};
+
+extern enum safe_crlf safe_crlf;
+
#define GIT_REPO_VERSION 0
extern int repository_format_version;
extern int check_repository_format(void);
extern int git_config_rename_section(const char *, const char *);
extern const char *git_etc_gitconfig(void);
extern int check_repository_format_version(const char *var, const char *value);
+extern int git_env_bool(const char *, int);
+extern int git_config_system(void);
+extern int git_config_global(void);
extern int config_error_nonbool(const char *);
#define MAX_GITNAME (1000)
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
+ extern int copy_file(const char *dst, const char *src, int mode);
extern int read_in_full(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
extern void write_or_die(int fd, const void *buf, size_t count);
/* convert.c */
/* returns 1 if *dst was used */
-extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
+extern int convert_to_git(const char *path, const char *src, size_t len,
+ struct strbuf *dst, enum safe_crlf checksafe);
extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
/* add */
#define WS_TRAILING_SPACE 01
#define WS_SPACE_BEFORE_TAB 02
#define WS_INDENT_WITH_NON_TAB 04
+#define WS_CR_AT_EOL 010
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);
FILE *stream, const char *set,
const char *reset, const char *ws);
extern char *whitespace_error_string(unsigned ws);
+extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
/* ls-files */
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
static int diff_detect_rename_default;
static int diff_rename_limit_default = 100;
-static int diff_use_color_default;
+int diff_use_color_default = -1;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
}
}
- return git_default_config(var, value);
+ return git_color_default_config(var, value);
}
static char *quote_two(const char *one, const char *two)
}
}
- static void copy_file(int prefix, const char *data, int size,
- const char *set, const char *reset)
+ static void copy_file_with_prefix(int prefix, const char *data, int size,
+ const char *set, const char *reset)
{
int ch, nl_just_seen = 1;
while (0 < size--) {
print_line_count(lc_b);
printf(" @@%s\n", reset);
if (lc_a)
- copy_file('-', one->data, one->size, old, reset);
+ copy_file_with_prefix('-', one->data, one->size, old, reset);
if (lc_b)
- copy_file('+', two->data, two->size, new, reset);
+ copy_file_with_prefix('+', two->data, two->size, new, reset);
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
}
}
+struct diffstat_dir {
+ struct diffstat_file **files;
+ int nr, percent, cumulative;
+};
+
+static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+{
+ unsigned long this_dir = 0;
+ unsigned int sources = 0;
+
+ while (dir->nr) {
+ struct diffstat_file *f = *dir->files;
+ int namelen = strlen(f->name);
+ unsigned long this;
+ char *slash;
+
+ if (namelen < baselen)
+ break;
+ if (memcmp(f->name, base, baselen))
+ break;
+ slash = strchr(f->name + baselen, '/');
+ if (slash) {
+ int newbaselen = slash + 1 - f->name;
+ this = gather_dirstat(dir, changed, f->name, newbaselen);
+ sources++;
+ } else {
+ if (f->is_unmerged || f->is_binary)
+ this = 0;
+ else
+ this = f->added + f->deleted;
+ dir->files++;
+ dir->nr--;
+ sources += 2;
+ }
+ this_dir += this;
+ }
+
+ /*
+ * We don't report dirstat's for
+ * - the top level
+ * - or cases where everything came from a single directory
+ * under this directory (sources == 1).
+ */
+ if (baselen && sources != 1) {
+ int permille = this_dir * 1000 / changed;
+ if (permille) {
+ int percent = permille / 10;
+ if (percent >= dir->percent) {
+ printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+ if (!dir->cumulative)
+ return 0;
+ }
+ }
+ }
+ return this_dir;
+}
+
+static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
+{
+ int i;
+ unsigned long changed;
+ struct diffstat_dir dir;
+
+ /* Calculate total changes */
+ changed = 0;
+ for (i = 0; i < data->nr; i++) {
+ if (data->files[i]->is_binary || data->files[i]->is_unmerged)
+ continue;
+ changed += data->files[i]->added;
+ changed += data->files[i]->deleted;
+ }
+
+ /* This can happen even with many files, if everything was renames */
+ if (!changed)
+ return;
+
+ /* Show all directories with more than x% of the changes */
+ dir.files = data->files;
+ dir.nr = data->nr;
+ dir.percent = options->dirstat_percent;
+ dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
+ gather_dirstat(&dir, changed, "", 0);
+}
+
static void free_diffstat_info(struct diffstat_t *diffstat)
{
int i;
"new\\|return\\|switch\\|throw\\|while\\)\n"
"^[ ]*\\(\\([ ]*"
"[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
- "[ ]*([^;]*$\\)" },
+ "[ ]*([^;]*\\)$" },
{ "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
};
if (pos < 0)
return 0;
ce = active_cache[pos];
- if ((lstat(name, &st) < 0) ||
- !S_ISREG(st.st_mode) || /* careful! */
- ce_match_stat(ce, &st, 0) ||
- hashcmp(sha1, ce->sha1))
+
+ /*
+ * This is not the sha1 we are looking for, or
+ * unreusable because it is not a regular file.
+ */
+ if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
return 0;
- /* we return 1 only when we can stat, it is a regular file,
- * stat information matches, and sha1 recorded in the cache
- * matches. I.e. we know the file in the work tree really is
- * the same as the <name, sha1> pair.
+
+ /*
+ * If ce matches the file in the work tree, we can reuse it.
*/
- return 1;
+ if (ce_uptodate(ce) ||
+ (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+ return 1;
+
+ return 0;
}
static int populate_from_stdin(struct diff_filespec *s)
* Convert from working tree format to canonical git format
*/
strbuf_init(&buf, 0);
- if (convert_to_git(s->path, s->data, s->size, &buf)) {
+ if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
size_t size = 0;
munmap(s->data, s->size);
s->should_munmap = 0;
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
+ options->dirstat_percent = 3;
options->context = 3;
options->msg_sep = "";
options->change = diff_change;
options->add_remove = diff_addremove;
- if (diff_use_color_default)
+ if (diff_use_color_default > 0)
DIFF_OPT_SET(options, COLOR_DIFF);
else
DIFF_OPT_CLR(options, COLOR_DIFF);
DIFF_FORMAT_NUMSTAT |
DIFF_FORMAT_DIFFSTAT |
DIFF_FORMAT_SHORTSTAT |
+ DIFF_FORMAT_DIRSTAT |
DIFF_FORMAT_SUMMARY |
DIFF_FORMAT_PATCH);
DIFF_FORMAT_NUMSTAT |
DIFF_FORMAT_DIFFSTAT |
DIFF_FORMAT_SHORTSTAT |
+ DIFF_FORMAT_DIRSTAT |
DIFF_FORMAT_SUMMARY |
DIFF_FORMAT_CHECKDIFF))
DIFF_OPT_SET(options, RECURSIVE);
options->output_format |= DIFF_FORMAT_NUMSTAT;
else if (!strcmp(arg, "--shortstat"))
options->output_format |= DIFF_FORMAT_SHORTSTAT;
+ else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
+ options->output_format |= DIFF_FORMAT_DIRSTAT;
+ else if (!strcmp(arg, "--cumulative"))
+ options->output_format |= DIFF_FORMAT_CUMULATIVE;
else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
separator++;
}
- if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
+ if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
struct diffstat_t diffstat;
memset(&diffstat, 0, sizeof(struct diffstat_t));
if (check_pair_status(p))
diff_flush_stat(p, options, &diffstat);
}
+ if (output_format & DIFF_FORMAT_DIRSTAT)
+ show_dirstat(&diffstat, options);
if (output_format & DIFF_FORMAT_NUMSTAT)
show_numstat(&diffstat, options);
if (output_format & DIFF_FORMAT_DIFFSTAT)