summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/git-read-tree.txt11
-rw-r--r--Documentation/git-write-tree.txt8
-rw-r--r--Makefile16
-rw-r--r--apply.c79
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-grep.c22
-rw-r--r--builtin-log.c161
-rw-r--r--builtin.h1
-rw-r--r--cache-tree.c557
-rw-r--r--cache-tree.h33
-rw-r--r--cache.h2
-rw-r--r--checkout-index.c1
-rw-r--r--commit.c36
-rw-r--r--commit.h3
-rw-r--r--contrib/remotes2config.sh35
-rw-r--r--date.c29
-rw-r--r--dump-cache-tree.c64
-rw-r--r--fsck-objects.c25
-rwxr-xr-xgit-am.sh38
-rwxr-xr-xgit-parse-remote.sh28
-rw-r--r--git.c1
-rw-r--r--log-tree.c45
-rw-r--r--read-cache.c72
-rw-r--r--read-tree.c132
-rw-r--r--rev-list.c2
-rw-r--r--revision.h1
-rw-r--r--show-branch.c2
-rwxr-xr-xt/t0000-basic.sh14
-rw-r--r--update-index.c13
-rw-r--r--write-tree.c166
31 files changed, 1373 insertions, 227 deletions
diff --git a/.gitignore b/.gitignore
index b5959d6311..7906909b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ git-write-tree
git-core-*/?*
test-date
test-delta
+test-dump-cache-tree
common-cmds.h
*.tar.gz
*.dsc
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 844cfda8d2..1f21d95684 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,7 +8,7 @@ git-read-tree - Reads tree information into the index
SYNOPSIS
--------
-'git-read-tree' (<tree-ish> | [[-m [--aggressive]| --reset] [-u | -i]] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
DESCRIPTION
@@ -63,6 +63,15 @@ OPTIONS
* when both sides adds a path identically. The resolution
is to add that path.
+--prefix=<prefix>/::
+ Keep the current index contents, and read the contents
+ of named tree-ish under directory at `<prefix>`. The
+ original index file cannot have anything at the path
+ `<prefix>` itself, and have nothing in `<prefix>/`
+ directory. Note that the `<prefix>/` value must end
+ with a slash.
+
+
<tree-ish#>::
The id of the tree object(s) to be read/merged.
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
index 77e12cb949..c85fa89c30 100644
--- a/Documentation/git-write-tree.txt
+++ b/Documentation/git-write-tree.txt
@@ -8,7 +8,7 @@ git-write-tree - Creates a tree object from the current index
SYNOPSIS
--------
-'git-write-tree' [--missing-ok]
+'git-write-tree' [--missing-ok] [--prefix=<prefix>/]
DESCRIPTION
-----------
@@ -30,6 +30,12 @@ OPTIONS
directory exist in the object database. This option disables this
check.
+--prefix=<prefix>/::
+ Writes a tree object that represents a subdirectory
+ `<prefix>`. This can be used to write the tree object
+ for a subproject that is in the named subdirectory.
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Makefile b/Makefile
index 9ba608c805..0aeb94c55d 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,8 @@ all:
#
# Define NO_MMAP if you want to avoid mmap.
#
+# Define NO_H_OPTION_IN_GREP if your grep does not understand -H.
+#
# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
#
# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
@@ -207,7 +209,7 @@ DIFF_OBJS = \
diffcore-delta.o log-tree.o
LIB_OBJS = \
- blob.o commit.o connect.o csum-file.o base85.o \
+ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
@@ -444,6 +446,12 @@ ifdef NO_ACCURATE_DIFF
ALL_CFLAGS += -DNO_ACCURATE_DIFF
endif
+ifdef NO_H_OPTION_IN_GREP
+ NO_H_OPTION_IN_GREP=1
+else
+ NO_H_OPTION_IN_GREP=0
+endif
+
# Shell quote (do not use $(call) to accomodate ancient setups);
SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
@@ -526,6 +534,9 @@ git$X git.spec \
%.o: %.S
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+builtin-grep.o: builtin-grep.c
+ $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_H_OPTION_IN_GREP=$(NO_H_OPTION_IN_GREP) $<
+
exec_cmd.o: exec_cmd.c
$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
@@ -610,6 +621,9 @@ test-date$X: test-date.c date.o ctype.o
test-delta$X: test-delta.c diff-delta.o patch-delta.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
check:
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
diff --git a/apply.c b/apply.c
index 7c8146a7f3..5bd5073365 100644
--- a/apply.c
+++ b/apply.c
@@ -8,6 +8,7 @@
*/
#include <fnmatch.h>
#include "cache.h"
+#include "cache-tree.h"
#include "quote.h"
#include "blob.h"
#include "delta.h"
@@ -17,6 +18,8 @@
// --stat does just a diffstat, and doesn't actually apply
// --numstat does numeric diffstat, and doesn't actually apply
// --index-info shows the old and new index info for paths if available.
+// --index updates the cache as well.
+// --cached updates only the cache without ever touching the working tree.
//
static const char *prefix;
static int prefix_length = -1;
@@ -26,6 +29,7 @@ static int p_value = 1;
static int allow_binary_replacement = 0;
static int check_index = 0;
static int write_index = 0;
+static int cached = 0;
static int diffstat = 0;
static int numstat = 0;
static int summary = 0;
@@ -36,7 +40,7 @@ static int show_index_info = 0;
static int line_termination = '\n';
static unsigned long p_context = -1;
static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
static enum whitespace_eol {
nowarn_whitespace,
@@ -1600,7 +1604,7 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
return 0;
}
-static int apply_data(struct patch *patch, struct stat *st)
+static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{
char *buf;
unsigned long size, alloc;
@@ -1609,7 +1613,17 @@ static int apply_data(struct patch *patch, struct stat *st)
size = 0;
alloc = 0;
buf = NULL;
- if (patch->old_name) {
+ if (cached) {
+ if (ce) {
+ char type[20];
+ buf = read_sha1_file(ce->sha1, type, &size);
+ if (!buf)
+ return error("read of %s failed",
+ patch->old_name);
+ alloc = size;
+ }
+ }
+ else if (patch->old_name) {
size = st->st_size;
alloc = size + 8192;
buf = xmalloc(alloc);
@@ -1637,16 +1651,21 @@ static int check_patch(struct patch *patch)
const char *old_name = patch->old_name;
const char *new_name = patch->new_name;
const char *name = old_name ? old_name : new_name;
+ struct cache_entry *ce = NULL;
if (old_name) {
- int changed;
- int stat_ret = lstat(old_name, &st);
+ int changed = 0;
+ int stat_ret = 0;
+ unsigned st_mode = 0;
+ if (!cached)
+ stat_ret = lstat(old_name, &st);
if (check_index) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0)
return error("%s: does not exist in index",
old_name);
+ ce = active_cache[pos];
if (stat_ret < 0) {
struct checkout costate;
if (errno != ENOENT)
@@ -1659,37 +1678,41 @@ static int check_patch(struct patch *patch)
costate.quiet = 0;
costate.not_new = 0;
costate.refresh_cache = 1;
- if (checkout_entry(active_cache[pos],
+ if (checkout_entry(ce,
&costate,
NULL) ||
lstat(old_name, &st))
return -1;
}
-
- changed = ce_match_stat(active_cache[pos], &st, 1);
+ if (!cached)
+ changed = ce_match_stat(ce, &st, 1);
if (changed)
return error("%s: does not match index",
old_name);
+ if (cached)
+ st_mode = ntohl(ce->ce_mode);
}
else if (stat_ret < 0)
return error("%s: %s", old_name, strerror(errno));
+ if (!cached)
+ st_mode = ntohl(create_ce_mode(st.st_mode));
+
if (patch->is_new < 0)
patch->is_new = 0;
- st.st_mode = ntohl(create_ce_mode(st.st_mode));
if (!patch->old_mode)
- patch->old_mode = st.st_mode;
- if ((st.st_mode ^ patch->old_mode) & S_IFMT)
+ patch->old_mode = st_mode;
+ if ((st_mode ^ patch->old_mode) & S_IFMT)
return error("%s: wrong type", old_name);
- if (st.st_mode != patch->old_mode)
+ if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
- old_name, st.st_mode, patch->old_mode);
+ old_name, st_mode, patch->old_mode);
}
if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
return error("%s: already exists in index", new_name);
- if (!lstat(new_name, &st))
+ if (!cached && !lstat(new_name, &st))
return error("%s: already exists in working directory", new_name);
if (errno != ENOENT)
return error("%s: %s", new_name, strerror(errno));
@@ -1709,9 +1732,9 @@ static int check_patch(struct patch *patch)
return error("new mode (%o) of %s does not match old mode (%o)%s%s",
patch->new_mode, new_name, patch->old_mode,
same ? "" : " of ", same ? "" : old_name);
- }
+ }
- if (apply_data(patch, &st) < 0)
+ if (apply_data(patch, &st, ce) < 0)
return error("%s: patch does not apply", name);
return 0;
}
@@ -1778,7 +1801,7 @@ static void numstat_patch_list(struct patch *patch)
{
for ( ; patch; patch = patch->next) {
const char *name;
- name = patch->old_name ? patch->old_name : patch->new_name;
+ name = patch->new_name ? patch->new_name : patch->old_name;
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
if (line_termination && quote_c_style(name, NULL, NULL, 0))
quote_c_style(name, NULL, stdout, 0);
@@ -1893,8 +1916,10 @@ static void remove_file(struct patch *patch)
if (write_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
+ cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
- unlink(patch->old_name);
+ if (!cached)
+ unlink(patch->old_name);
}
static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
@@ -1911,9 +1936,11 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = htons(namelen);
- if (lstat(path, &st) < 0)
- die("unable to stat newly created file %s", path);
- fill_stat_cache_info(ce, &st);
+ if (!cached) {
+ if (lstat(path, &st) < 0)
+ die("unable to stat newly created file %s", path);
+ fill_stat_cache_info(ce, &st);
+ }
if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
die("unable to create backing store for newly created file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
@@ -1950,6 +1977,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
*/
static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
{
+ if (cached)
+ return;
if (!try_create_file(path, mode, buf, size))
return;
@@ -1989,8 +2018,9 @@ static void create_file(struct patch *patch)
if (!mode)
mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
+ create_one_file(path, mode, buf, size);
add_index_file(path, mode, buf, size);
+ cache_tree_invalidate_path(active_cache_tree, path);
}
static void write_out_one_result(struct patch *patch)
@@ -2182,6 +2212,11 @@ int main(int argc, char **argv)
check_index = 1;
continue;
}
+ if (!strcmp(arg, "--cached")) {
+ check_index = 1;
+ cached = 1;
+ continue;
+ }
if (!strcmp(arg, "--apply")) {
apply = 1;
continue;
diff --git a/builtin-diff.c b/builtin-diff.c
index b0c9cade02..95d2c699b9 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs,
return 0;
}
-static void add_head(struct rev_info *revs)
+void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;
diff --git a/builtin-grep.c b/builtin-grep.c
index 66111de514..36512d8a17 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -453,7 +453,6 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
len = nr = 0;
push_arg("grep");
- push_arg("-H");
if (opt->fixed)
push_arg("-F");
if (opt->linenum)
@@ -503,7 +502,13 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
push_arg("-e");
push_arg(p->pattern);
}
- push_arg("--");
+
+ if (NO_H_OPTION_IN_GREP)
+ push_arg("/dev/null");
+ else {
+ push_arg("-H");
+ push_arg("--");
+ }
hit = 0;
argc = nr;
@@ -535,8 +540,19 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
* Use the external "grep" command for the case where
* we grep through the checked-out files. It tends to
* be a lot more optimized
+ *
+ * Some grep implementations do not understand -H nor --
+ * but /dev/null can be used as a substitution in most
+ * cases.
+ *
+ * However -L and -c would slightly misbehave (-L would
+ * list /dev/null as a hit, and -c would report 0 hits
+ * from /dev/null); so do not use the external one on
+ * such platforms.
*/
- if (!cached) {
+ if (!cached &&
+ (!NO_H_OPTION_IN_GREP ||
+ (!opt->count && !opt->unmatch_name_only))) {
hit = external_grep(opt, paths, cached);
if (hit >= 0)
return hit;
diff --git a/builtin-log.c b/builtin-log.c
index 69f2911cb4..d5bbc1cc06 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -9,6 +9,10 @@
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
+#include "builtin.h"
+
+/* this is in builtin-diff.c */
+void add_head(struct rev_info *revs);
static int cmd_log_wc(int argc, const char **argv, char **envp,
struct rev_info *rev)
@@ -67,3 +71,160 @@ int cmd_log(int argc, const char **argv, char **envp)
rev.diffopt.recursive = 1;
return cmd_log_wc(argc, argv, envp, &rev);
}
+
+static int istitlechar(char c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || c == '.' || c == '_';
+}
+
+static FILE *realstdout = NULL;
+static char *output_directory = NULL;
+
+static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
+{
+ char filename[1024];
+ char *sol;
+ int len = 0;
+
+ if (output_directory) {
+ strncpy(filename, output_directory, 1010);
+ len = strlen(filename);
+ if (filename[len - 1] != '/')
+ filename[len++] = '/';
+ }
+
+ sprintf(filename + len, "%04d", nr);
+ len = strlen(filename);
+
+ sol = strstr(commit->buffer, "\n\n");
+ if (sol) {
+ int j, space = 1;
+
+ sol += 2;
+ /* strip [PATCH] or [PATCH blabla] */
+ if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
+ char *eos = strchr(sol + 6, ']');
+ if (eos) {
+ while (isspace(*eos))
+ eos++;
+ sol = eos;
+ }
+ }
+
+ for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
+ if (istitlechar(sol[j])) {
+ if (space) {
+ filename[len++] = '-';
+ space = 0;
+ }
+ filename[len++] = sol[j];
+ if (sol[j] == '.')
+ while (sol[j + 1] == '.')
+ j++;
+ } else
+ space = 1;
+ }
+ while (filename[len - 1] == '.' || filename[len - 1] == '-')
+ len--;
+ }
+ strcpy(filename + len, ".txt");
+ fprintf(realstdout, "%s\n", filename);
+ freopen(filename, "w", stdout);
+}
+
+int cmd_format_patch(int argc, const char **argv, char **envp)
+{
+ struct commit *commit;
+ struct commit **list = NULL;
+ struct rev_info rev;
+ int nr = 0, total, i, j;
+ int use_stdout = 0;
+ int numbered = 0;
+ int keep_subject = 0;
+
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_EMAIL;
+ rev.verbose_header = 1;
+ rev.diff = 1;
+ rev.diffopt.with_raw = 0;
+ rev.diffopt.with_stat = 1;
+ rev.combine_merges = 0;
+ rev.ignore_merges = 1;
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ /*
+ * Parse the arguments before setup_revisions(), or something
+ * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
+ * possibly a valid SHA1.
+ */
+ for (i = 1, j = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--stdout"))
+ use_stdout = 1;
+ else if (!strcmp(argv[i], "-n") ||
+ !strcmp(argv[i], "--numbered"))
+ numbered = 1;
+ else if (!strcmp(argv[i], "-k") ||
+ !strcmp(argv[i], "--keep-subject")) {
+ keep_subject = 1;
+ rev.total = -1;
+ } else if (!strcmp(argv[i], "-o")) {
+ if (argc < 3)
+ die ("Which directory?");
+ if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
+ die("Could not create directory %s",
+ argv[i + 1]);
+ output_directory = strdup(argv[i + 1]);
+ i++;
+ } else
+ argv[j++] = argv[i];
+ }
+ argc = j;
+
+ if (numbered && keep_subject < 0)
+ die ("-n and -k are mutually exclusive.");
+
+ argc = setup_revisions(argc, argv, &rev, "HEAD");
+ if (argc > 1)
+ die ("unrecognized argument: %s", argv[1]);
+
+ if (rev.pending_objects && rev.pending_objects->next == NULL) {
+ rev.pending_objects->item->flags |= UNINTERESTING;
+ add_head(&rev);
+ }
+
+ if (!use_stdout)
+ realstdout = fdopen(dup(1), "w");
+
+ prepare_revision_walk(&rev);
+ while ((commit = get_revision(&rev)) != NULL) {
+ /* ignore merges */
+ if (commit->parents && commit->parents->next)
+ continue;
+ nr++;
+ list = realloc(list, nr * sizeof(list[0]));
+ list[nr - 1] = commit;
+ }
+ total = nr;
+ if (numbered)
+ rev.total = total;
+ while (0 <= --nr) {
+ int shown;
+ commit = list[nr];
+ rev.nr = total - nr;
+ if (!use_stdout)
+ reopen_stdout(commit, rev.nr, keep_subject);
+ shown = log_tree_commit(&rev, commit);
+ free(commit->buffer);
+ commit->buffer = NULL;
+ if (shown)
+ printf("-- \n%s\n\n", git_version_string);
+ if (!use_stdout)
+ fclose(stdout);
+ }
+ if (output_directory)
+ free(output_directory);
+ free(list);
+ return 0;
+}
+
diff --git a/builtin.h b/builtin.h
index 7744f7d2f6..97e2464468 100644
--- a/builtin.h
+++ b/builtin.h
@@ -20,6 +20,7 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
extern int cmd_diff(int argc, const char **argv, char **envp);
+extern int cmd_format_patch(int argc, const char **argv, char **envp);
extern int cmd_count_objects(int argc, const char **argv, char **envp);
extern int cmd_push(int argc, const char **argv, char **envp);
diff --git a/cache-tree.c b/cache-tree.c
new file mode 100644
index 0000000000..d9f7e1e3dd
--- /dev/null
+++ b/cache-tree.c
@@ -0,0 +1,557 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+#define DEBUG 0
+
+struct cache_tree *cache_tree(void)
+{
+ struct cache_tree *it = xcalloc(1, sizeof(struct cache_tree));
+ it->entry_count = -1;
+ return it;
+}
+
+void cache_tree_free(struct cache_tree **it_p)
+{
+ int i;
+ struct cache_tree *it = *it_p;
+
+ if (!it)
+ return;
+ for (i = 0; i < it->subtree_nr; i++)
+ if (it->down[i])
+ cache_tree_free(&it->down[i]->cache_tree);
+ free(it->down);
+ free(it);
+ *it_p = NULL;
+}
+
+static int subtree_name_cmp(const char *one, int onelen,
+ const char *two, int twolen)
+{
+ if (onelen < twolen)
+ return -1;
+ if (twolen < onelen)
+ return 1;
+ return memcmp(one, two, onelen);
+}
+
+static int subtree_pos(struct cache_tree *it, const char *path, int pathlen)
+{
+ struct cache_tree_sub **down = it->down;
+ int lo, hi;
+ lo = 0;
+ hi = it->subtree_nr;
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ struct cache_tree_sub *mdl = down[mi];
+ int cmp = subtree_name_cmp(path, pathlen,
+ mdl->name, mdl->namelen);
+ if (!cmp)
+ return mi;
+ if (cmp < 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+ return -lo-1;
+}
+
+static struct cache_tree_sub *find_subtree(struct cache_tree *it,
+ const char *path,
+ int pathlen,
+ int create)
+{
+ struct cache_tree_sub *down;
+ int pos = subtree_pos(it, path, pathlen);
+ if (0 <= pos)
+ return it->down[pos];
+ if (!create)
+ return NULL;
+
+ pos = -pos-1;
+ if (it->subtree_alloc <= it->subtree_nr) {
+ it->subtree_alloc = alloc_nr(it->subtree_alloc);
+ it->down = xrealloc(it->down, it->subtree_alloc *
+ sizeof(*it->down));
+ }
+ it->subtree_nr++;
+
+ down = xmalloc(sizeof(*down) + pathlen + 1);
+ down->cache_tree = NULL;
+ down->namelen = pathlen;
+ memcpy(down->name, path, pathlen);
+ down->name[pathlen] = 0;
+
+ if (pos < it->subtree_nr)
+ memmove(it->down + pos + 1,
+ it->down + pos,
+ sizeof(down) * (it->subtree_nr - pos - 1));
+ it->down[pos] = down;
+ return down;
+}
+
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
+{
+ int pathlen = strlen(path);
+ return find_subtree(it, path, pathlen, 1);
+}
+
+void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
+{
+ /* a/b/c
+ * ==> invalidate self
+ * ==> find "a", have it invalidate "b/c"
+ * a
+ * ==> invalidate self
+ * ==> if "a" exists as a subtree, remove it.
+ */
+ const char *slash;
+ int namelen;
+ struct cache_tree_sub *down;
+
+#if DEBUG
+ fprintf(stderr, "cache-tree invalidate <%s>\n", path);
+#endif
+
+ if (!it)
+ return;
+ slash = strchr(path, '/');
+ it->entry_count = -1;
+ if (!slash) {
+ int pos;
+ namelen = strlen(path);
+ pos = subtree_pos(it, path, namelen);
+ if (0 <= pos) {
+ cache_tree_free(&it->down[pos]->cache_tree);
+ free(it->down[pos]);
+ /* 0 1 2 3 4 5
+ * ^ ^subtree_nr = 6
+ * pos
+ * move 4 and 5 up one place (2 entries)
+ * 2 = 6 - 3 - 1 = subtree_nr - pos - 1
+ */
+ memmove(it->down+pos, it->down+pos+1,
+ sizeof(struct cache_tree_sub *) *
+ (it->subtree_nr - pos - 1));
+ it->subtree_nr--;
+ }
+ return;
+ }
+ namelen = slash - path;
+ down = find_subtree(it, path, namelen, 0);
+ if (down)
+ cache_tree_invalidate_path(down->cache_tree, slash + 1);
+}
+
+static int verify_cache(struct cache_entry **cache,
+ int entries)
+{
+ int i, funny;
+
+ /* Verify that the tree is merged */
+ funny = 0;
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ if (ce_stage(ce)) {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "%s: unmerged (%s)\n",
+ ce->name, sha1_to_hex(ce->sha1));
+ }
+ }
+ if (funny)
+ return -1;
+
+ /* Also verify that the cache does not have path and path/file
+ * at the same time. At this point we know the cache has only
+ * stage 0 entries.
+ */
+ funny = 0;
+ for (i = 0; i < entries - 1; i++) {
+ /* path/file always comes after path because of the way
+ * the cache is sorted. Also path can appear only once,
+ * which means conflicting one would immediately follow.
+ */
+ const char *this_name = cache[i]->name;
+ const char *next_name = cache[i+1]->name;
+ int this_len = strlen(this_name);
+ if (this_len < strlen(next_name) &&
+ strncmp(this_name, next_name, this_len) == 0 &&
+ next_name[this_len] == '/') {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "You have both %s and %s\n",
+ this_name, next_name);
+ }
+ }
+ if (funny)
+ return -1;
+ return 0;
+}
+
+static void discard_unused_subtrees(struct cache_tree *it)
+{
+ struct cache_tree_sub **down = it->down;
+ int nr = it->subtree_nr;
+ int dst, src;
+ for (dst = src = 0; src < nr; src++) {
+ struct cache_tree_sub *s = down[src];
+ if (s->used)
+ down[dst++] = s;
+ else {
+ cache_tree_free(&s->cache_tree);
+ free(s);
+ it->subtree_nr--;
+ }
+ }
+}
+
+int cache_tree_fully_valid(struct cache_tree *it)
+{
+ int i;
+ if (!it)
+ return 0;
+ if (it->entry_count < 0 || !has_sha1_file(it->sha1))
+ return 0;
+ for (i = 0; i < it->subtree_nr; i++) {
+ if (!cache_tree_fully_valid(it->down[i]->cache_tree))
+ return 0;
+ }
+ return 1;
+}
+
+static int update_one(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ const char *base,
+ int baselen,
+ int missing_ok,
+ int dryrun)
+{
+ unsigned long size, offset;
+ char *buffer;
+ int i;
+
+ if (0 <= it->entry_count && has_sha1_file(it->sha1))
+ return it->entry_count;
+
+ /*
+ * We first scan for subtrees and update them; we start by
+ * marking existing subtrees -- the ones that are unmarked
+ * should not be in the result.
+ */
+ for (i = 0; i < it->subtree_nr; i++)
+ it->down[i]->used = 0;
+
+ /*
+ * Find the subtrees and update them.
+ */
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, sublen, subcnt;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (!slash)
+ continue;
+ /*
+ * a/bbb/c (base = a/, slash = /c)
+ * ==>
+ * path+baselen = bbb/c, sublen = 3
+ */
+ sublen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, sublen, 1);
+ if (!sub->cache_tree)
+ sub->cache_tree = cache_tree();
+ subcnt = update_one(sub->cache_tree,
+ cache + i, entries - i,
+ path,
+ baselen + sublen + 1,
+ missing_ok,
+ dryrun);
+ i += subcnt - 1;
+ sub->used = 1;
+ }
+
+ discard_unused_subtrees(it);
+
+ /*
+ * Then write out the tree object for this level.
+ */
+ size = 8192;
+ buffer = xmalloc(size);
+ offset = 0;
+
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, entlen;
+ const unsigned char *sha1;
+ unsigned mode;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (slash) {
+ entlen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, entlen, 0);
+ if (!sub)
+ die("cache-tree.c: '%.*s' in '%s' not found",
+ entlen, path + baselen, path);
+ i += sub->cache_tree->entry_count - 1;
+ sha1 = sub->cache_tree->sha1;
+ mode = S_IFDIR;
+ }
+ else {
+ sha1 = ce->sha1;
+ mode = ntohl(ce->ce_mode);
+ entlen = pathlen - baselen;
+ }
+ if (!missing_ok && !has_sha1_file(sha1))
+ return error("invalid object %s", sha1_to_hex(sha1));
+
+ if (!ce->ce_mode)
+ continue; /* entry being removed */
+
+ if (size < offset + entlen + 100) {
+ size = alloc_nr(offset + entlen + 100);
+ buffer = xrealloc(buffer, size);
+ }
+ offset += sprintf(buffer + offset,
+ "%o %.*s", mode, entlen, path + baselen);
+ buffer[offset++] = 0;
+ memcpy(buffer + offset, sha1, 20);
+ offset += 20;
+
+#if DEBUG
+ fprintf(stderr, "cache-tree update-one %o %.*s\n",
+ mode, entlen, path + baselen);
+#endif
+ }
+
+ if (dryrun) {
+ unsigned char hdr[200];
+ int hdrlen;
+ write_sha1_file_prepare(buffer, offset, tree_type, it->sha1,
+ hdr, &hdrlen);
+ }
+ else
+ write_sha1_file(buffer, offset, tree_type, it->sha1);
+ free(buffer);
+ it->entry_count = i;
+#if DEBUG
+ fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
+ it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+#endif
+ return i;
+}
+
+int cache_tree_update(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ int missing_ok,
+ int dryrun)
+{
+ int i;
+ i = verify_cache(cache, entries);
+ if (i)
+ return i;
+ i = update_one(it, cache, entries, "", 0, missing_ok, dryrun);
+ if (i < 0)
+ return i;
+ return 0;
+}
+
+static void *write_one(struct cache_tree *it,
+ char *path,
+ int pathlen,
+ char *buffer,
+ unsigned long *size,
+ unsigned long *offset)
+{
+ int i;
+
+ /* One "cache-tree" entry consists of the following:
+ * path (NUL terminated)
+ * entry_count, subtree_nr ("%d %d\n")
+ * tree-sha1 (missing if invalid)
+ * subtree_nr "cache-tree" entries for subtrees.
+ */
+ if (*size < *offset + pathlen + 100) {
+ *size = alloc_nr(*offset + pathlen + 100);
+ buffer = xrealloc(buffer, *size);
+ }
+ *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n",
+ pathlen, path, 0,
+ it->entry_count, it->subtree_nr);
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
+ pathlen, path, it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n",
+ pathlen, path, it->subtree_nr);
+#endif
+
+ if (0 <= it->entry_count) {
+ memcpy(buffer + *offset, it->sha1, 20);
+ *offset += 20;
+ }
+ for (i = 0; i < it->subtree_nr; i++) {
+ struct cache_tree_sub *down = it->down[i];
+ if (i) {
+ struct cache_tree_sub *prev = it->down[i-1];
+ if (subtree_name_cmp(down->name, down->namelen,
+ prev->name, prev->namelen) <= 0)
+ die("fatal - unsorted cache subtree");
+ }
+ buffer = write_one(down->cache_tree, down->name, down->namelen,
+ buffer, size, offset);
+ }
+ return buffer;
+}
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p)
+{
+ char path[PATH_MAX];
+ unsigned long size = 8192;
+ char *buffer = xmalloc(size);
+
+ *size_p = 0;
+ path[0] = 0;
+ return write_one(root, path, 0, buffer, &size, size_p);
+}
+
+static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
+{
+ const char *buf = *buffer;
+ unsigned long size = *size_p;
+ const char *cp;
+ char *ep;
+ struct cache_tree *it;
+ int i, subtree_nr;
+
+ it = NULL;
+ /* skip name, but make sure name exists */
+ while (size && *buf) {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ it = cache_tree();
+
+ cp = buf;
+ it->entry_count = strtol(cp, &ep, 10);
+ if (cp == ep)
+ goto free_return;
+ cp = ep;
+ subtree_nr = strtol(cp, &ep, 10);
+ if (cp == ep)
+ goto free_return;
+ while (size && *buf && *buf != '\n') {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ if (0 <= it->entry_count) {
+ if (size < 20)
+ goto free_return;
+ memcpy(it->sha1, buf, 20);
+ buf += 20;
+ size -= 20;
+ }
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
+ *buffer, it->entry_count, subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n",
+ *buffer, subtree_nr);
+#endif
+
+ /*
+ * Just a heuristic -- we do not add directories that often but
+ * we do not want to have to extend it immediately when we do,
+ * hence +2.
+ */
+ it->subtree_alloc = subtree_nr + 2;
+ it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *));
+ for (i = 0; i < subtree_nr; i++) {
+ /* read each subtree */
+ struct cache_tree *sub;
+ struct cache_tree_sub *subtree;
+ const char *name = buf;
+
+ sub = read_one(&buf, &size);
+ if (!sub)
+ goto free_return;
+ subtree = cache_tree_sub(it, name);
+ subtree->cache_tree = sub;
+ }
+ if (subtree_nr != it->subtree_nr)
+ die("cache-tree: internal error");
+ *buffer = buf;
+ *size_p = size;
+ return it;
+
+ free_return:
+ cache_tree_free(&it);
+ return NULL;
+}
+
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
+{
+ if (buffer[0])
+ return NULL; /* not the whole tree */
+ return read_one(&buffer, &size);
+}
+
+struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
+{
+ while (*path) {
+ const char *slash;
+ struct cache_tree_sub *sub;
+
+ slash = strchr(path, '/');
+ if (!slash)
+ slash = path + strlen(path);
+ /* between path and slash is the name of the
+ * subtree to look for.
+ */
+ sub = find_subtree(it, path, slash - path, 0);
+ if (!sub)
+ return NULL;
+ it = sub->cache_tree;
+ if (slash)
+ while (*slash && *slash == '/')
+ slash++;
+ if (!slash || !*slash)
+ return it; /* prefix ended with slashes */
+ path = slash;
+ }
+ return it;
+}
diff --git a/cache-tree.h b/cache-tree.h
new file mode 100644
index 0000000000..119407e3a1
--- /dev/null
+++ b/cache-tree.h
@@ -0,0 +1,33 @@
+#ifndef CACHE_TREE_H
+#define CACHE_TREE_H
+
+struct cache_tree;
+struct cache_tree_sub {
+ struct cache_tree *cache_tree;
+ int namelen;
+ int used;
+ char name[FLEX_ARRAY];
+};
+
+struct cache_tree {
+ int entry_count; /* negative means "invalid" */
+ unsigned char sha1[20];
+ int subtree_nr;
+ int subtree_alloc;
+ struct cache_tree_sub **down;
+};
+
+struct cache_tree *cache_tree(void);
+void cache_tree_free(struct cache_tree **);
+void cache_tree_invalidate_path(struct cache_tree *, const char *);
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p);
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
+
+int cache_tree_fully_valid(struct cache_tree *);
+int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
+
+struct cache_tree *cache_tree_find(struct cache_tree *, const char *);
+
+#endif
diff --git a/cache.h b/cache.h
index 4b7a439253..b1300cd989 100644
--- a/cache.h
+++ b/cache.h
@@ -114,6 +114,7 @@ static inline unsigned int create_ce_mode(unsigned int mode)
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
+extern struct cache_tree *active_cache_tree;
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@ -251,6 +252,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned char *sha1_ret);
const char *show_date(unsigned long time, int timezone);
+const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
diff --git a/checkout-index.c b/checkout-index.c
index cc3a745c14..9876af6fd6 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -39,6 +39,7 @@
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#define CHECKOUT_ALL 4
static const char *prefix;
diff --git a/commit.c b/commit.c
index 4a26070c13..84558bac29 100644
--- a/commit.c
+++ b/commit.c
@@ -30,6 +30,7 @@ struct cmt_fmt_map {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
+ { "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
@@ -438,6 +439,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
+ if (fmt == CMIT_FMT_EMAIL) {
+ what = "From";
+ filler = "";
+ }
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
@@ -445,6 +450,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
+ case CMIT_FMT_EMAIL:
+ ret += sprintf(buf + ret, "Date: %s\n",
+ show_rfc2822_date(time, tz));
+ break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
@@ -455,10 +464,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
return ret;
}
-static int is_empty_line(const char *line, int len)
+static int is_empty_line(const char *line, int *len_p)
{
+ int len = *len_p;
while (len && isspace(line[len-1]))
len--;
+ *len_p = len;
return !len;
}
@@ -467,7 +478,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
struct commit_list *parent = commit->parents;
int offset;
- if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+ if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+ !parent || !parent->next)
return 0;
offset = sprintf(buf, "Merge:");
@@ -486,14 +498,17 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
return offset;
}
-unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
+unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject)
{
int hdr = 1, body = 0;
unsigned long offset = 0;
- int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+ int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ indent = 0;
+
for (;;) {
const char *line = msg;
int linelen = get_one_line(msg, len);
@@ -516,7 +531,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
if (hdr) {
if (linelen == 1) {
hdr = 0;
- if (fmt != CMIT_FMT_ONELINE)
+ if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
@@ -554,20 +569,29 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
continue;
}
- if (is_empty_line(line, linelen)) {
+ if (is_empty_line(line, &linelen)) {
if (!body)
continue;
+ if (subject)
+ continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}
+ if (subject) {
+ int slen = strlen(subject);
+ memcpy(buf + offset, subject, slen);
+ offset += slen;
+ }
memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
+ buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
break;
+ subject = NULL;
}
while (offset && isspace(buf[offset-1]))
offset--;
diff --git a/commit.h b/commit.h
index de142afe73..8d7514cd00 100644
--- a/commit.h
+++ b/commit.h
@@ -45,12 +45,13 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
+ CMIT_FMT_EMAIL,
CMIT_FMT_UNSPECIFIED,
};
extern enum cmit_fmt get_commit_format(const char *arg);
-extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev);
+extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject);
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.
diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh
new file mode 100644
index 0000000000..25901e2b3b
--- /dev/null
+++ b/contrib/remotes2config.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Use this tool to rewrite your .git/remotes/ files into the config.
+
+. git-sh-setup
+
+if [ -d "$GIT_DIR"/remotes ]; then
+ echo "Rewriting $GIT_DIR/remotes" >&2
+ error=0
+ # rewrite into config
+ {
+ cd "$GIT_DIR"/remotes
+ ls | while read f; do
+ name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".")
+ sed -n \
+ -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \
+ -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \
+ -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \
+ < "$f"
+ done
+ echo done
+ } | while read key value regex; do
+ case $key in
+ done)
+ if [ $error = 0 ]; then
+ mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old
+ fi ;;
+ *)
+ echo "git-repo-config $key "$value" $regex"
+ git-repo-config $key "$value" $regex || error=1 ;;
+ esac
+ done
+fi
+
+
diff --git a/date.c b/date.c
index 034d7228bf..365dc3b14e 100644
--- a/date.c
+++ b/date.c
@@ -42,18 +42,24 @@ static const char *weekday_names[] = {
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
-const char *show_date(unsigned long time, int tz)
+static struct tm *time_to_tm(unsigned long time, int tz)
{
- struct tm *tm;
time_t t;
- static char timebuf[200];
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
t = time + minutes * 60;
- tm = gmtime(&t);
+ return gmtime(&t);
+}
+
+const char *show_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
@@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz)
return timebuf;
}
+const char *show_rfc2822_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
+ if (!tm)
+ return NULL;
+ sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+ weekday_names[tm->tm_wday], tm->tm_mday,
+ month_names[tm->tm_mon], tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
+ return timebuf;
+}
+
/*
* Check these. And note how it doesn't do the summer-time conversion.
*
diff --git a/dump-cache-tree.c b/dump-cache-tree.c
new file mode 100644
index 0000000000..1ccaf51773
--- /dev/null
+++ b/dump-cache-tree.c
@@ -0,0 +1,64 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+
+static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
+{
+ if (it->entry_count < 0)
+ printf("%-40s %s%s (%d subtrees)\n",
+ "invalid", x, pfx, it->subtree_nr);
+ else
+ printf("%s %s%s (%d entries, %d subtrees)\n",
+ sha1_to_hex(it->sha1), x, pfx,
+ it->entry_count, it->subtree_nr);
+}
+
+static int dump_cache_tree(struct cache_tree *it,
+ struct cache_tree *ref,
+ const char *pfx)
+{
+ int i;
+ int errs = 0;
+
+ if (!it || !ref)
+ /* missing in either */
+ return 0;
+
+ if (it->entry_count < 0) {
+ dump_one(it, pfx, "");
+ dump_one(ref, pfx, "#(ref) ");
+ if (it->subtree_nr != ref->subtree_nr)
+ errs = 1;
+ }
+ else {
+ dump_one(it, pfx, "");
+ if (memcmp(it->sha1, ref->sha1, 20) ||
+ ref->entry_count != it->entry_count ||
+ ref->subtree_nr != it->subtree_nr) {
+ dump_one(ref, pfx, "#(ref) ");
+ errs = 1;
+ }
+ }
+
+ for (i = 0; i < it->subtree_nr; i++) {
+ char path[PATH_MAX];
+ struct cache_tree_sub *down = it->down[i];
+ struct cache_tree_sub *rdwn;
+
+ rdwn = cache_tree_sub(ref, down->name);
+ sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
+ if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
+ errs = 1;
+ }
+ return errs;
+}
+
+int main(int ac, char **av)
+{
+ struct cache_tree *another = cache_tree();
+ if (read_cache() < 0)
+ die("unable to read index file");
+ cache_tree_update(another, active_cache, active_nr, 0, 1);
+ return dump_cache_tree(active_cache_tree, another, "");
+}
diff --git a/fsck-objects.c b/fsck-objects.c
index 59b25904cb..1922b6d84c 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -8,6 +8,7 @@
#include "tag.h"
#include "refs.h"
#include "pack.h"
+#include "cache-tree.h"
#define REACHABLE 0x0001
@@ -438,6 +439,28 @@ static int fsck_head_link(void)
return 0;
}
+static int fsck_cache_tree(struct cache_tree *it)
+{
+ int i;
+ int err = 0;
+
+ if (0 <= it->entry_count) {
+ struct object *obj = parse_object(it->sha1);
+ if (!obj) {
+ error("%s: invalid sha1 pointer in cache-tree",
+ sha1_to_hex(it->sha1));
+ return 1;
+ }
+ mark_reachable(obj, REACHABLE);
+ obj->used = 1;
+ if (obj->type != tree_type)
+ err |= objerror(obj, "non-tree in cache-tree");
+ }
+ for (i = 0; i < it->subtree_nr; i++)
+ err |= fsck_cache_tree(it->down[i]->cache_tree);
+ return err;
+}
+
int main(int argc, char **argv)
{
int i, heads;
@@ -547,6 +570,8 @@ int main(int argc, char **argv)
obj->used = 1;
mark_reachable(obj, REACHABLE);
}
+ if (active_cache_tree)
+ fsck_cache_tree(active_cache_tree);
}
check_connectivity();
diff --git a/git-am.sh b/git-am.sh
index 33f208cb0b..97ec2d0c7d 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -59,46 +59,12 @@ fall_back_3way () {
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-write-tree >"$dotest/patch-merge-base+" &&
# index has the base tree now.
- (
- cd "$dotest/patch-merge-tmp-dir" &&
- GIT_INDEX_FILE="../patch-merge-tmp-index" \
- GIT_OBJECT_DIRECTORY="$O_OBJECT" \
- git-apply $binary --index <../patch
- )
+ GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+ git-apply $binary --cached <"$dotest/patch"
then
echo Using index info to reconstruct a base tree...
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
- else
- # Otherwise, try nearby trees that can be used to apply the
- # patch.
- (
- N=10
-
- # Hoping the patch is against our recent commits...
- git-rev-list --max-count=$N HEAD
-
- # or hoping the patch is against known tags...
- git-ls-remote --tags .
- ) |
- while read base junk
- do
- # See if we have it as a tree...
- git-cat-file tree "$base" >/dev/null 2>&1 || continue
-
- rm -fr "$dotest"/patch-merge-* &&
- mkdir "$dotest/patch-merge-tmp-dir" || break
- (
- cd "$dotest/patch-merge-tmp-dir" &&
- GIT_INDEX_FILE=../patch-merge-tmp-index &&
- GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
- export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
- git-read-tree "$base" &&
- git-apply $binary --index &&
- mv ../patch-merge-tmp-index ../patch-merge-index &&
- echo "$base" >../patch-merge-base
- ) <"$dotest/patch" 2>/dev/null && break
- done
fi
test -f "$dotest/patch-merge-index" &&
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index c9b899e3d7..187f0883c9 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -10,7 +10,10 @@ get_data_source () {
# Not so fast. This could be the partial URL shorthand...
token=$(expr "z$1" : 'z\([^/]*\)/')
remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
- if test -f "$GIT_DIR/branches/$token"
+ if test "$(git-repo-config --get "remote.$token.url")"
+ then
+ echo config-partial
+ elif test -f "$GIT_DIR/branches/$token"
then
echo branches-partial
else
@@ -18,7 +21,10 @@ get_data_source () {
fi
;;
*)
- if test -f "$GIT_DIR/remotes/$1"
+ if test "$(git-repo-config --get "remote.$1.url")"
+ then
+ echo config
+ elif test -f "$GIT_DIR/remotes/$1"
then
echo remotes
elif test -f "$GIT_DIR/branches/$1"
@@ -35,6 +41,15 @@ get_remote_url () {
case "$data_source" in
'')
echo "$1" ;;
+ config-partial)
+ token=$(expr "z$1" : 'z\([^/]*\)/')
+ remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
+ url=$(git-repo-config --get "remote.$token.url")
+ echo "$url/$remainder"
+ ;;
+ config)
+ git-repo-config --get "remote.$1.url"
+ ;;
remotes)
sed -ne '/^URL: */{
s///p
@@ -56,8 +71,10 @@ get_remote_url () {
get_remote_default_refs_for_push () {
data_source=$(get_data_source "$1")
case "$data_source" in
- '' | branches | branches-partial)
+ '' | config-partial | branches | branches-partial)
;; # no default push mapping, just send matching refs.
+ config)
+ git-repo-config --get-all "remote.$1.push" ;;
remotes)
sed -ne '/^Push: */{
s///p
@@ -111,8 +128,11 @@ canon_refs_list_for_fetch () {
get_remote_default_refs_for_fetch () {
data_source=$(get_data_source "$1")
case "$data_source" in
- '' | branches-partial)
+ '' | config-partial | branches-partial)
echo "HEAD:" ;;
+ config)
+ canon_refs_list_for_fetch \
+ $(git-repo-config --get-all "remote.$1.fetch") ;;
branches)
remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
case "$remote_branch" in '') remote_branch=master ;; esac
diff --git a/git.c b/git.c
index a94d9ee5a7..84803a62e6 100644
--- a/git.c
+++ b/git.c
@@ -47,6 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
{ "push", cmd_push },
+ { "fmt-patch", cmd_format_patch },
{ "count-objects", cmd_count_objects },
{ "diff", cmd_diff },
{ "grep", cmd_grep },
diff --git a/log-tree.c b/log-tree.c
index b90ba6762a..526d578e98 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
const char *extra;
int len;
+ char* subject = NULL;
opt->loginfo = NULL;
if (!opt->verbose_header) {
@@ -49,19 +50,38 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
/*
* Print header line of header..
*/
- printf("%s%s",
- opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
- diff_unique_abbrev(commit->object.sha1, abbrev_commit));
- if (opt->parents)
- show_parents(commit, abbrev_commit);
- if (parent)
- printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+
+ if (opt->commit_format == CMIT_FMT_EMAIL) {
+ if (opt->total > 0) {
+ static char buffer[64];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [PATCH %d/%d] ",
+ opt->nr, opt->total);
+ subject = buffer;
+ } else if (opt->total == 0)
+ subject = "Subject: [PATCH] ";
+ else
+ subject = "Subject: ";
+
+ printf("From %s Thu Apr 7 15:13:13 2005\n",
+ sha1_to_hex(commit->object.sha1));
+ } else {
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ if (parent)
+ printf(" (from %s)",
+ diff_unique_abbrev(parent->object.sha1,
+ abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ }
/*
* And then the pretty-printed message itself
*/
- len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);
+ len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject);
printf("%s%s%s", this_header, extra, sep);
}
@@ -166,15 +186,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
struct log_info log;
+ int shown;
log.commit = commit;
log.parent = NULL;
opt->loginfo = &log;
- if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+ shown = log_tree_diff(opt, commit, &log);
+ if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt, opt->loginfo, "");
+ shown = 1;
}
opt->loginfo = NULL;
- return 0;
+ return shown;
}
diff --git a/read-cache.c b/read-cache.c
index a917ab0cfe..ed0da38e07 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -4,11 +4,26 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "cache-tree.h"
+
+/* Index extensions.
+ *
+ * The first letter should be 'A'..'Z' for extensions that are not
+ * necessary for a correct operation (i.e. optimization data).
+ * When new extensions are added that _needs_ to be understood in
+ * order to correctly interpret the index file, pick character that
+ * is outside the range, to cause the reader to abort.
+ */
+
+#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
+#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
struct cache_entry **active_cache = NULL;
static time_t index_file_timestamp;
unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
+struct cache_tree *active_cache_tree = NULL;
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -513,6 +528,22 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
return 0;
}
+static int read_index_extension(const char *ext, void *data, unsigned long sz)
+{
+ switch (CACHE_EXT(ext)) {
+ case CACHE_EXT_TREE:
+ active_cache_tree = cache_tree_read(data, sz);
+ break;
+ default:
+ if (*ext < 'A' || 'Z' < *ext)
+ return error("index uses %.4s extension, which we do not understand",
+ ext);
+ fprintf(stderr, "ignoring %.4s extension\n", ext);
+ break;
+ }
+ return 0;
+}
+
int read_cache(void)
{
int fd, i;
@@ -561,6 +592,22 @@ int read_cache(void)
active_cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
+ while (offset <= size - 20 - 8) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ unsigned long extsize;
+ memcpy(&extsize, map + offset + 4, 4);
+ extsize = ntohl(extsize);
+ if (read_index_extension(map + offset,
+ map + offset + 8, extsize) < 0)
+ goto unmap;
+ offset += 8;
+ offset += extsize;
+ }
return active_nr;
unmap:
@@ -595,6 +642,17 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
return 0;
}
+static int write_index_ext_header(SHA_CTX *context, int fd,
+ unsigned long ext, unsigned long sz)
+{
+ ext = htonl(ext);
+ sz = htonl(sz);
+ if ((ce_write(context, fd, &ext, 4) < 0) ||
+ (ce_write(context, fd, &sz, 4) < 0))
+ return -1;
+ return 0;
+}
+
static int ce_flush(SHA_CTX *context, int fd)
{
unsigned int left = write_buffer_len;
@@ -691,5 +749,19 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
return -1;
}
+
+ /* Write extension data here */
+ if (active_cache_tree) {
+ unsigned long sz;
+ void *data = cache_tree_write(active_cache_tree, &sz);
+ if (data &&
+ !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
+ !ce_write(&c, newfd, data, sz))
+ ;
+ else {
+ free(data);
+ return -1;
+ }
+ }
return ce_flush(&c, newfd);
}
diff --git a/read-tree.c b/read-tree.c
index e16e91b173..aa6172b52e 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -9,6 +9,7 @@
#include "object.h"
#include "tree.h"
+#include "cache-tree.h"
#include <sys/time.h>
#include <signal.h>
@@ -21,6 +22,7 @@ static int trivial_merges_only = 0;
static int aggressive = 0;
static int verbose_update = 0;
static volatile int progress_update = 0;
+static const char *prefix = NULL;
static int head_idx = -1;
static int merge_size = 0;
@@ -369,7 +371,8 @@ static int unpack_trees(merge_fn_t fn)
posns[i] = ((struct tree *) posn->item)->entries;
posn = posn->next;
}
- if (unpack_trees_rec(posns, len, "", fn, &indpos))
+ if (unpack_trees_rec(posns, len, prefix ? prefix : "",
+ fn, &indpos))
return -1;
}
@@ -426,6 +429,12 @@ static void verify_uptodate(struct cache_entry *ce)
die("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
+static void invalidate_ce_path(struct cache_entry *ce)
+{
+ if (ce)
+ cache_tree_invalidate_path(active_cache_tree, ce->name);
+}
+
static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
{
merge->ce_flags |= htons(CE_UPDATE);
@@ -441,8 +450,11 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
*merge = *old;
} else {
verify_uptodate(old);
+ invalidate_ce_path(old);
}
}
+ else
+ invalidate_ce_path(merge);
merge->ce_flags &= ~htons(CE_STAGEMASK);
add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
return 1;
@@ -454,6 +466,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
verify_uptodate(old);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+ invalidate_ce_path(ce);
return 1;
}
@@ -674,6 +687,28 @@ static int twoway_merge(struct cache_entry **src)
}
/*
+ * Bind merge.
+ *
+ * Keep the index entries at stage0, collapse stage1 but make sure
+ * stage0 does not have anything in prefix.
+ */
+static int bind_merge(struct cache_entry **src)
+{
+ struct cache_entry *old = src[0];
+ struct cache_entry *a = src[1];
+
+ if (merge_size != 1)
+ return error("Cannot do a bind merge of %d trees\n",
+ merge_size);
+ if (!a)
+ return merged_entry(old, NULL);
+ if (old)
+ die("Entry '%s' overlaps. Cannot bind.", a->name);
+
+ return merged_entry(a, NULL);
+}
+
+/*
* One-way merge.
*
* The rule is:
@@ -688,8 +723,10 @@ static int oneway_merge(struct cache_entry **src)
return error("Cannot do a oneway merge of %d trees",
merge_size);
- if (!a)
+ if (!a) {
+ invalidate_ce_path(old);
return deleted_entry(old, NULL);
+ }
if (old && same(old, a)) {
if (reset) {
struct stat st;
@@ -714,6 +751,7 @@ static int read_cache_unmerged(void)
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
deleted++;
+ invalidate_ce_path(ce);
continue;
}
if (deleted)
@@ -724,7 +762,40 @@ static int read_cache_unmerged(void)
return deleted;
}
-static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive] [-u | -i] <sha1> [<sha2> [<sha3>]])";
+static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
+{
+ struct tree_entry_list *ent;
+ int cnt;
+
+ memcpy(it->sha1, tree->object.sha1, 20);
+ for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
+ if (!ent->directory)
+ cnt++;
+ else {
+ struct cache_tree_sub *sub;
+ struct tree *subtree = (struct tree *)ent->item.tree;
+ if (!subtree->object.parsed)
+ parse_tree(subtree);
+ sub = cache_tree_sub(it, ent->name);
+ sub->cache_tree = cache_tree();
+ prime_cache_tree_rec(sub->cache_tree, subtree);
+ cnt += sub->cache_tree->entry_count;
+ }
+ }
+ it->entry_count = cnt;
+}
+
+static void prime_cache_tree(void)
+{
+ struct tree *tree = (struct tree *)trees->item;
+ if (!tree)
+ return;
+ active_cache_tree = cache_tree();
+ prime_cache_tree_rec(active_cache_tree, tree);
+
+}
+
+static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <sha1> [<sha2> [<sha3>]])";
static struct cache_file cache_file;
@@ -769,9 +840,24 @@ int main(int argc, char **argv)
continue;
}
+ /* "--prefix=<subdirectory>/" means keep the current index
+ * entries and put the entries from the tree under the
+ * given subdirectory.
+ */
+ if (!strncmp(arg, "--prefix=", 9)) {
+ if (stage || merge || prefix)
+ usage(read_tree_usage);
+ prefix = arg + 9;
+ merge = 1;
+ stage = 1;
+ if (read_cache_unmerged())
+ die("you need to resolve your current index first");
+ continue;
+ }
+
/* This differs from "-m" in that we'll silently ignore unmerged entries */
if (!strcmp(arg, "--reset")) {
- if (stage || merge)
+ if (stage || merge || prefix)
usage(read_tree_usage);
reset = 1;
merge = 1;
@@ -792,7 +878,7 @@ int main(int argc, char **argv)
/* "-m" stands for "merge", meaning we start in stage 1 */
if (!strcmp(arg, "-m")) {
- if (stage || merge)
+ if (stage || merge || prefix)
usage(read_tree_usage);
if (read_cache_unmerged())
die("you need to resolve your current index first");
@@ -814,21 +900,39 @@ int main(int argc, char **argv)
if ((update||index_only) && !merge)
usage(read_tree_usage);
+ if (prefix) {
+ int pfxlen = strlen(prefix);
+ int pos;
+ if (prefix[pfxlen-1] != '/')
+ die("prefix must end with /");
+ if (stage != 2)
+ die("binding merge takes only one tree");
+ pos = cache_name_pos(prefix, pfxlen);
+ if (0 <= pos)
+ die("corrupt index file");
+ pos = -pos-1;
+ if (pos < active_nr &&
+ !strncmp(active_cache[pos]->name, prefix, pfxlen))
+ die("subdirectory '%s' already exists.", prefix);
+ pos = cache_name_pos(prefix, pfxlen-1);
+ if (0 <= pos)
+ die("file '%.*s' already exists.", pfxlen-1, prefix);
+ }
+
if (merge) {
if (stage < 2)
die("just how do you expect me to merge %d trees?", stage-1);
switch (stage - 1) {
case 1:
- fn = oneway_merge;
+ fn = prefix ? bind_merge : oneway_merge;
break;
case 2:
fn = twoway_merge;
break;
case 3:
- fn = threeway_merge;
- break;
default:
fn = threeway_merge;
+ cache_tree_free(&active_cache_tree);
break;
}
@@ -839,6 +943,18 @@ int main(int argc, char **argv)
}
unpack_trees(fn);
+
+ /*
+ * When reading only one tree (either the most basic form,
+ * "-m ent" or "--reset ent" form), we can obtain a fully
+ * valid cache-tree because the index must match exactly
+ * what came from the tree.
+ */
+ if (trees && trees->item && (!merge || (stage == 2))) {
+ cache_tree_free(&active_cache_tree);
+ prime_cache_tree();
+ }
+
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("unable to write new index file");
diff --git a/rev-list.c b/rev-list.c
index 8b0ec388fa..235ae4c7e1 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -84,7 +84,7 @@ static void show_commit(struct commit *commit)
static char pretty_header[16384];
pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header),
- revs.abbrev);
+ revs.abbrev, NULL);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);
diff --git a/revision.h b/revision.h
index 48d7b4ca94..62759f7bc0 100644
--- a/revision.h
+++ b/revision.h
@@ -58,6 +58,7 @@ struct rev_info {
unsigned int abbrev;
enum cmit_fmt commit_format;
struct log_info *loginfo;
+ int nr, total;
/* special limits */
int max_count;
diff --git a/show-branch.c b/show-branch.c
index 268c57b180..bbe26c2e7a 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name)
struct commit_name *name = commit->object.util;
if (commit->object.parsed)
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
- pretty, sizeof(pretty), 0);
+ pretty, sizeof(pretty), 0, NULL);
else
strcpy(pretty, "(unavailable)");
if (!strncmp(pretty, "[PATCH] ", 8))
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index cf33989b56..2c9bbb59b0 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -195,6 +195,20 @@ test_expect_success \
'git-ls-tree -r output for a known tree.' \
'diff current expected'
+test_expect_success \
+ 'writing partial tree out with git-write-tree --prefix.' \
+ 'ptree=$(git-write-tree --prefix=path3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3'
+
+test_expect_success \
+ 'writing partial tree out with git-write-tree --prefix.' \
+ 'ptree=$(git-write-tree --prefix=path3/subp3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2'
+
################################################################
rm .git/index
test_expect_success \
diff --git a/update-index.c b/update-index.c
index 3d7e02db2c..f6b09a4800 100644
--- a/update-index.c
+++ b/update-index.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#include "tree-walk.h"
/*
@@ -71,6 +72,7 @@ static int mark_valid(const char *path)
active_cache[pos]->ce_flags &= ~htons(CE_VALID);
break;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
return 0;
}
@@ -84,6 +86,12 @@ static int add_file_to_cache(const char *path)
struct stat st;
status = lstat(path, &st);
+
+ /* We probably want to do this in remove_file_from_cache() and
+ * add_cache_entry() instead...
+ */
+ cache_tree_invalidate_path(active_cache_tree, path);
+
if (status < 0 || S_ISDIR(st.st_mode)) {
/* When we used to have "path" and now we want to add
* "path/file", we need a way to remove "path" before
@@ -326,6 +334,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
+ cache_tree_invalidate_path(active_cache_tree, path);
return 0;
}
@@ -350,6 +359,7 @@ static void chmod_path(int flip, const char *path)
default:
goto fail;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
report("chmod %cx '%s'", flip, path);
return;
@@ -371,6 +381,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
die("Unable to mark file %s", path);
goto free_return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
if (force_remove) {
if (remove_file_from_cache(p))
@@ -449,6 +460,7 @@ static void read_index_info(int line_termination)
free(path_name);
continue;
}
+ cache_tree_invalidate_path(active_cache_tree, path_name);
if (!mode) {
/* mode == 0 means there is no such path -- remove */
@@ -555,6 +567,7 @@ static int unresolve_one(const char *path)
goto free_return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
remove_file_from_cache(path);
if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
error("%s: cannot add our version to the index.", path);
diff --git a/write-tree.c b/write-tree.c
index dcad6e6670..895e7a359d 100644
--- a/write-tree.c
+++ b/write-tree.c
@@ -5,154 +5,68 @@
*/
#include "cache.h"
#include "tree.h"
+#include "cache-tree.h"
static int missing_ok = 0;
+static char *prefix = NULL;
-static int check_valid_sha1(unsigned char *sha1)
-{
- int ret;
-
- /* If we were anal, we'd check that the sha1 of the contents actually matches */
- ret = has_sha1_file(sha1);
- if (ret == 0)
- perror(sha1_file_name(sha1));
- return ret ? 0 : -1;
-}
-
-static int write_tree(struct cache_entry **cachep, int maxentries, const char *base, int baselen, unsigned char *returnsha1)
-{
- unsigned char subdir_sha1[20];
- unsigned long size, offset;
- char *buffer;
- int nr;
-
- /* Guess at some random initial size */
- size = 8192;
- buffer = xmalloc(size);
- offset = 0;
-
- nr = 0;
- while (nr < maxentries) {
- struct cache_entry *ce = cachep[nr];
- const char *pathname = ce->name, *filename, *dirname;
- int pathlen = ce_namelen(ce), entrylen;
- unsigned char *sha1;
- unsigned int mode;
-
- /* Did we hit the end of the directory? Return how many we wrote */
- if (baselen >= pathlen || memcmp(base, pathname, baselen))
- break;
-
- sha1 = ce->sha1;
- mode = ntohl(ce->ce_mode);
-
- /* Do we have _further_ subdirectories? */
- filename = pathname + baselen;
- dirname = strchr(filename, '/');
- if (dirname) {
- int subdir_written;
-
- subdir_written = write_tree(cachep + nr, maxentries - nr, pathname, dirname-pathname+1, subdir_sha1);
- nr += subdir_written;
-
- /* Now we need to write out the directory entry into this tree.. */
- mode = S_IFDIR;
- pathlen = dirname - pathname;
-
- /* ..but the directory entry doesn't count towards the total count */
- nr--;
- sha1 = subdir_sha1;
- }
+static const char write_tree_usage[] =
+"git-write-tree [--missing-ok] [--prefix=<prefix>/]";
- if (!missing_ok && check_valid_sha1(sha1) < 0)
- exit(1);
-
- entrylen = pathlen - baselen;
- if (offset + entrylen + 100 > size) {
- size = alloc_nr(offset + entrylen + 100);
- buffer = xrealloc(buffer, size);
- }
- offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename);
- buffer[offset++] = 0;
- memcpy(buffer + offset, sha1, 20);
- offset += 20;
- nr++;
- }
-
- write_sha1_file(buffer, offset, tree_type, returnsha1);
- free(buffer);
- return nr;
-}
-
-static const char write_tree_usage[] = "git-write-tree [--missing-ok]";
+static struct cache_file cache_file;
int main(int argc, char **argv)
{
- int i, funny;
- int entries;
- unsigned char sha1[20];
-
+ int entries, was_valid, newfd;
+
setup_git_directory();
+ newfd = hold_index_file_for_update(&cache_file, get_index_file());
entries = read_cache();
- if (argc == 2) {
- if (!strcmp(argv[1], "--missing-ok"))
+
+ while (1 < argc) {
+ char *arg = argv[1];
+ if (!strcmp(arg, "--missing-ok"))
missing_ok = 1;
+ else if (!strncmp(arg, "--prefix=", 9))
+ prefix = arg + 9;
else
die(write_tree_usage);
+ argc--; argv++;
}
-
+
if (argc > 2)
die("too many options");
if (entries < 0)
die("git-write-tree: error reading cache");
- /* Verify that the tree is merged */
- funny = 0;
- for (i = 0; i < entries; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce)) {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "%s: unmerged (%s)\n", ce->name, sha1_to_hex(ce->sha1));
+ if (!active_cache_tree)
+ active_cache_tree = cache_tree();
+
+ was_valid = cache_tree_fully_valid(active_cache_tree);
+ if (!was_valid) {
+ if (cache_tree_update(active_cache_tree,
+ active_cache, active_nr,
+ missing_ok, 0) < 0)
+ die("git-write-tree: error building trees");
+ if (0 <= newfd) {
+ if (!write_cache(newfd, active_cache, active_nr))
+ commit_index_file(&cache_file);
}
- }
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Also verify that the cache does not have path and path/file
- * at the same time. At this point we know the cache has only
- * stage 0 entries.
- */
- funny = 0;
- for (i = 0; i < entries - 1; i++) {
- /* path/file always comes after path because of the way
- * the cache is sorted. Also path can appear only once,
- * which means conflicting one would immediately follow.
+ /* Not being able to write is fine -- we are only interested
+ * in updating the cache-tree part, and if the next caller
+ * ends up using the old index with unupdated cache-tree part
+ * it misses the work we did here, but that is just a
+ * performance penalty and not a big deal.
*/
- const char *this_name = active_cache[i]->name;
- const char *next_name = active_cache[i+1]->name;
- int this_len = strlen(this_name);
- if (this_len < strlen(next_name) &&
- strncmp(this_name, next_name, this_len) == 0 &&
- next_name[this_len] == '/') {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "You have both %s and %s\n",
- this_name, next_name);
- }
}
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Ok, write it out */
- if (write_tree(active_cache, entries, "", 0, sha1) != entries)
- die("git-write-tree: internal error");
- printf("%s\n", sha1_to_hex(sha1));
+ if (prefix) {
+ struct cache_tree *subtree =
+ cache_tree_find(active_cache_tree, prefix);
+ printf("%s\n", sha1_to_hex(subtree->sha1));
+ }
+ else
+ printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
return 0;
}