* Re: [PATCH] move get_merge_bases() to core lib; use it in merge-recursive
From: Alex Riesen @ 2006-06-30 0:27 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.63.0606292050380.29667@wbgn013.biozentrum.uni-wuerzburg.de>
Johannes Schindelin, Thu, Jun 29, 2006 21:06:56 +0200:
>
> Okay. Convinced.
>
> I tested my patch again, and like Alex said, a test fails. But I tested on
> top of Alex's latest merge-recursive patch, which has that nasty
> update-index bug, and that is the reason for the test to fail.
>
> So, a few tests later, I am pretty sure that my patches do not break
> git-merge-base. I'll prepare another patch series which builds-in
> merge-base.
>
Just for reference, I'll repost all my patches again, somewhat
consolidated. Your merge_base patches can be applied on top of that
series.
^ permalink raw reply
* [PATCH 2/4] make filepairs detachable
From: Alex Riesen @ 2006-06-30 0:27 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Junio C Hamano
Actually, just make sure diff_flush does not crash for diff queue
entries which were cleared.
---
diff.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/diff.c b/diff.c
index 5a71489..b3480f6 100644
--- a/diff.c
+++ b/diff.c
@@ -2281,6 +2281,8 @@ void diff_flush(struct diff_options *opt
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
+ if (!p)
+ continue;
flush_one_pair(p, diff_output_format, options, diffstat);
}
@@ -2290,6 +2292,8 @@ void diff_flush(struct diff_options *opt
}
for (i = 0; i < q->nr; i++) {
+ if (!q->queue[i])
+ continue;
if (diffstat && options->summary)
diff_summary(q->queue[i]);
diff_free_filepair(q->queue[i]);
--
1.4.1.rc1.g17dc
^ permalink raw reply related
* [PATCH 1/4] merge-recursive in C
From: Alex Riesen @ 2006-06-30 0:27 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Junio C Hamano
This is summary of all merge-recursive patches floating around rebased
off current git (75dedd5a21246be03ae443e9fc6a9f75c6d2995b).
---
I think I have to start doing this properly sometime, so that
incremental patches can be posted. So I just chose a compiling state
(it still has that ctime/mtime bug), added some of recent work on top
of it and branched it all off Junio's master.
The merge_bases patches from Dscho ("refactor merge_bases()" and "move
get_merge_bases() to core lib") can be applied on top of last.
Makefile | 7
git-merge.sh | 4
graph.c | 256 ++++++++
graph.h | 80 +++
merge-recursive.c | 1622 +++++++++++++++++++++++++++++++++++++++++++++++++++++
path-list.c | 110 ++++
path-list.h | 32 +
7 files changed, 2108 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
index cde619c..660f09b 100644
--- a/Makefile
+++ b/Makefile
@@ -163,7 +163,8 @@ PROGRAMS = \
git-upload-pack$X git-verify-pack$X \
git-symbolic-ref$X \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
- git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
+ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+ git-merge-recur$X
BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
@@ -595,6 +596,10 @@ git-http-push$X: revision.o http.o http-
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+git-merge-recur$X: merge-recursive.o graph.o path-list.o $(LIB_FILE)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS)
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h
diff --git a/git-merge.sh b/git-merge.sh
index 24e3b50..3c3fed6 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -9,7 +9,7 @@ USAGE='[-n] [--no-commit] [--squash] [-s
LF='
'
-all_strategies='recursive octopus resolve stupid ours'
+all_strategies='recur recursive octopus resolve stupid ours'
default_twohead_strategies='recursive'
default_octopus_strategies='octopus'
no_trivial_merge_strategies='ours'
@@ -17,7 +17,7 @@ use_strategies=
index_merge=t
if test "@@NO_PYTHON@@"; then
- all_strategies='resolve octopus stupid ours'
+ all_strategies='recur resolve octopus stupid ours'
default_twohead_strategies='resolve'
fi
diff --git a/graph.c b/graph.c
new file mode 100644
index 0000000..1a28302
--- /dev/null
+++ b/graph.c
@@ -0,0 +1,256 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include "cache.h"
+#include "commit.h"
+#include "graph.h"
+
+/* does not belong here */
+struct tree *git_write_tree()
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n",
+ getenv("GIT_INDEX_FILE"));
+#endif
+ FILE *fp = popen("git-write-tree 2>/dev/null", "r");
+ char buf[41];
+ unsigned char sha1[20];
+ int ch;
+ unsigned i = 0;
+ while ( (ch = fgetc(fp)) != EOF )
+ if ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' )
+ buf[i++] = ch;
+ else
+ break;
+ int rc = pclose(fp);
+ if ( rc == -1 || WEXITSTATUS(rc) )
+ return NULL;
+ buf[i] = '\0';
+ if ( get_sha1(buf, sha1) != 0 )
+ return NULL;
+ return lookup_tree(sha1);
+}
+
+const char *node_title(struct node *node, int *len)
+{
+ const char *s = "(null commit)";
+ *len = strlen(s);
+
+ if ( node->virtual ) {
+ s = node->comment;
+ *len = strlen(s);
+ } else if ( node->commit ) {
+ if ( parse_commit(node->commit) != 0 ) {
+ s = "(bad commit)";
+ *len = strlen(s);
+ } else {
+ s = node->commit->buffer;
+ char prev = '\0';
+ while ( *s ) {
+ if ( '\n' == prev && '\n' == *s ) {
+ ++s;
+ break;
+ }
+ prev = *s++;
+ }
+ *len = 0;
+ while ( s[*len] && '\n' != s[*len] )
+ ++(*len);
+ }
+ }
+ return s;
+}
+
+const unsigned char *node_sha(const struct node *node)
+{
+ return node->commit->object.sha1;
+}
+
+const char *node_hex_sha1(const struct node *node)
+{
+ return node->commit ? sha1_to_hex(node->commit->object.sha1):
+ node->virtual ? "virtual": "undefined";
+}
+
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+ if ( !a && !b )
+ return 2;
+ return a && b && memcmp(a, b, 20) == 0;
+}
+
+static struct node_list *node_list_pool = NULL;
+static unsigned node_list_pool_size = 0;
+
+unsigned node_list_count(const struct node_list *l)
+{
+ unsigned c = 0;
+ while ( l ) {
+ ++c;
+ l = l->next;
+ }
+ return c;
+}
+
+struct node_list *node_list_insert(struct node *node, struct node_list **list)
+{
+ struct node_list *e;
+ if ( node_list_pool )
+ e = node_list_shift(&node_list_pool);
+ else
+ e = malloc(sizeof(struct node_list));
+ e->next = *list;
+ e->node = node;
+ *list = e;
+ return e;
+}
+
+struct node_list *node_list_free1(struct node_list *list, int free_nodes)
+{
+ struct node_list *next = list->next;
+ if ( free_nodes )
+ free(list->node);
+
+ if ( node_list_pool_size < 1000 ) {
+ list->node = NULL;
+ list->next = node_list_pool;
+ node_list_pool = list;
+ } else
+ free(list);
+ return next;
+}
+
+void node_list_free(struct node_list **list, int free_nodes)
+{
+ while ( *list )
+ node_list_free1(node_list_shift(list), free_nodes);
+}
+
+struct node *node_alloc(struct commit *commit)
+{
+ struct node *node = malloc(sizeof(struct node));
+ node->parents = NULL;
+ node->parents_count = 0;
+ node->children = NULL;
+ node->virtual = 0;
+ node->commit = commit;
+ if ( parse_commit(commit) == 0 )
+ node->tree = commit->tree;
+ else
+ die("failed to parse commit %s", sha1_to_hex(commit->object.sha1));
+
+ return node;
+}
+
+struct node *node_alloc_virtual(struct tree *tree, const char *comment)
+{
+ struct node *node = malloc(sizeof(struct node));
+ node->parents = NULL;
+ node->children = NULL;
+ node->commit = NULL;
+ node->tree = tree;
+ node->virtual = 1;
+ static unsigned virtual_id = 0;
+ node->virtual_id = virtual_id++;
+ node->comment = comment;
+ return node;
+}
+
+void node_set_parents(struct node *node, struct node_list *parents)
+{
+ struct node_list *p;
+ node->parents_count = 0;
+ for_each_node_list(p, node->parents = parents) {
+ node_list_insert(node, &p->node->children);
+ node->parents_count++;
+ }
+}
+
+static inline int node_eq(const struct node *a, const struct node *b)
+{
+ if ( a == b )
+ return 1;
+ if ( a->virtual != b->virtual )
+ return 0;
+ if ( a->virtual )
+ return a->virtual_id == b->virtual_id;
+ if ( a->commit == b->commit )
+ return 1;
+ return sha_eq(a->commit->object.sha1, b->commit->object.sha1);
+}
+
+struct node_list *node_list_find_node(const struct node *node,
+ const struct node_list *list)
+{
+ const struct node_list *p;
+ for_each_node_list(p, list)
+ if ( node_eq(p->node, node) )
+ break;
+ return (struct node_list*)p;
+}
+
+/*
+ * a & b. a and are invalid after the call,
+ * the result will contain all the common nodes
+ */
+struct node_list *node_list_intersect(struct node_list *a,
+ struct node_list *b)
+{
+ struct node_list *result = NULL;
+ struct node_list *p = a;
+ while ( p ) {
+ struct node_list *next = p->next;
+ struct node_list *bn = node_list_find_node(p->node, b);
+ if ( bn ) {
+ p->next = result;
+ result = p;
+ } else
+ node_list_free1(p, 0);
+ p = next;
+ }
+ node_list_free(&b, 0);
+ return result;
+}
+
+struct node *graph_add_node(struct graph *graph, struct node *node)
+{
+ struct node_list **bucket;
+ if ( node->virtual )
+ /* virtual nodes hashed by lowest byte of virtual_id */
+ bucket = graph->commits + (node->virtual_id & 0xff);
+ else
+ bucket = graph->commits + node->commit->object.sha1[0];
+ node_list_insert(node, bucket);
+ return node;
+}
+
+struct node *graph_node_bysha(const struct graph *graph,
+ const unsigned char *sha)
+{
+ const struct node_list *head = *(graph->commits + sha[0]);
+ while ( head ) {
+ if ( !head->node->virtual &&
+ sha_eq(head->node->commit->object.sha1, sha) ) {
+ return head->node;
+ }
+ head = head->next;
+ }
+ return NULL;
+}
+
+#if 0
+void node_list_print(const char *msg, const struct node_list *list)
+{
+ const struct node_list *p;
+ printf("%s\n", msg);
+ for_each_node_list(p, list) {
+ int len;
+ const char *msg = node_title(p->node, &len);
+ printf("\t%s %.*s\n", node_hex_sha1(p->node), len, msg);
+ }
+}
+#endif
+
+/*
+vim: sw=8 noet
+*/
diff --git a/graph.h b/graph.h
new file mode 100644
index 0000000..3b8ca5c
--- /dev/null
+++ b/graph.h
@@ -0,0 +1,80 @@
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+struct node;
+struct graph;
+struct commit;
+struct tree;
+struct commit_list;
+
+struct node_list
+{
+ struct node_list *next;
+ struct node *node;
+};
+
+struct node
+{
+ struct commit *commit;
+ struct tree *tree;
+
+ unsigned parents_count;
+ struct node_list *parents;
+ struct node_list *children;
+
+ unsigned virtual:1;
+ unsigned virtual_id;
+ const char *comment;
+};
+
+struct node_list *node_list_insert(struct node *node, struct node_list **list);
+void node_list_free(struct node_list **list, int free_nodes);
+struct node_list *node_list_free1(struct node_list *list, int free_node);
+
+static inline struct node_list *node_list_shift(struct node_list **list)
+{
+ struct node_list *head = *list;
+ *list = head->next;
+ return head;
+}
+
+static inline struct node *node_list_shift_node(struct node_list **list)
+{
+ struct node *node = (*list)->node;
+ *list = node_list_free1(*list, 0);
+ return node;
+}
+
+struct node *node_alloc(struct commit *);
+struct node *node_alloc_virtual(struct tree *, const char *comment);
+
+void node_set_parents(struct node *node, struct node_list *parents);
+unsigned node_list_count(const struct node_list *);
+
+struct node_list *node_list_find_node(const struct node *node,
+ const struct node_list *list);
+
+
+struct graph
+{
+ // hashed by first byte of SHA-1 or low byte of virtual_id
+ struct node_list *commits[256];
+};
+
+struct node *graph_add_node(struct graph *graph, struct node *node);
+struct node *graph_node_bysha(const struct graph *graph,
+ const unsigned char *sha);
+
+#define for_each_node_list(p,list) \
+ for ( p = (list); p; p = p->next )
+
+const char *node_title(struct node *, int *len);
+const unsigned char *node_sha(const struct node *);
+const char *node_hex_sha1(const struct node *);
+void node_list_print(const char *msg, const struct node_list *list);
+
+struct tree *git_write_tree();
+
+#endif /* _GRAPH_H_ */
+
+// vim: sw=8 noet
diff --git a/merge-recursive.c b/merge-recursive.c
new file mode 100644
index 0000000..abceb30
--- /dev/null
+++ b/merge-recursive.c
@@ -0,0 +1,1622 @@
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "run-command.h"
+
+#include "graph.h"
+#include "path-list.h"
+
+#define HAVE_ALLOCA
+#define DEBUG
+
+#ifdef DEBUG
+#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)
+#else
+#define debug(args, ...)
+#endif
+
+#define for_each_commit(p,list) for ( p = (list); p; p = p->next )
+
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+ if ( !a && !b )
+ return 2;
+ return a && b && memcmp(a, b, 20) == 0;
+}
+
+static void memswp(void *p1, void *p2, unsigned n)
+{
+ unsigned char *a = p1, *b = p2;
+ while ( n-- ) {
+ *a ^= *b;
+ *b ^= *a;
+ *a ^= *b;
+ ++a;
+ ++b;
+ }
+}
+
+
+struct merge_result
+{
+ struct node *commit;
+ unsigned clean:1;
+};
+
+struct merge_tree_result
+{
+ struct tree *tree;
+ unsigned clean:1;
+};
+
+static struct merge_tree_result merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1Name,
+ const char *branch2Name);
+
+struct index_entry
+{
+ struct index_entry *next;
+ struct
+ {
+ unsigned mode;
+ unsigned char sha[20];
+ } stages[4];
+ unsigned processed:1;
+ char path[1];
+};
+
+struct index_entry *index_entry_alloc(const char *path)
+{
+ size_t n = strlen(path); /* index_entry::path has room for \0 */
+ struct index_entry *p = xmalloc(sizeof(struct index_entry) + n);
+ if ( !p )
+ return NULL;
+ memcpy(p->path, path, n + 1);
+ p->next = NULL;
+ p->processed = 0;
+ return p;
+}
+
+#if 0
+static
+void print_index_entry(const char *text, const struct index_entry *e)
+{
+ printf("%s%s next: %p %s\n", text,
+ e ? e->path: NULL,
+ e ? e->next: NULL,
+ e && e->processed ? "processed": "");
+ if ( e ) {
+ int i;
+ for ( i = 1; i < 4; ++i )
+ printf("\tstage[%d]: %06o %s\n", i,
+ e->stages[i].mode,
+ sha1_to_hex(e->stages[i].sha));
+ }
+}
+#endif
+
+static struct path_list currentFileSet = {NULL, 0, 0};
+static struct path_list currentDirectorySet = {NULL, 0, 0};
+
+static int output_indent = 0;
+
+static void output(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ for ( i = output_indent; i--; )
+ fputs(" ", stdout);
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ fputc('\n', stdout);
+}
+
+static const char *original_index_file;
+static const char *temporary_index_file;
+
+static void setup_index(int temp)
+{
+ const char *idx = temp ? temporary_index_file: original_index_file;
+ unlink(temporary_index_file);
+ setenv("GIT_INDEX_FILE", idx, 1);
+}
+
+/*
+ * This is a global variable which is used in a number of places but
+ * only written to in the 'merge' function.
+ *
+ * index_only == 1 => Don't leave any non-stage 0 entries in the cache and
+ * don't update the working directory.
+ * 0 => Leave unmerged entries in the cache and update
+ * the working directory.
+ */
+static int index_only = 0;
+
+static int git_read_tree(const struct tree *tree)
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s\n",
+ getenv("GIT_INDEX_FILE"),
+ sha1_to_hex(tree->object.sha1));
+#endif
+ const char *argv[] = { "git-read-tree", NULL, NULL, };
+ argv[1] = sha1_to_hex(tree->object.sha1);
+ int rc = run_command_v(2, argv);
+ return rc < 0 ? -1: rc;
+}
+
+static int git_merge_trees(const char *update_arg,
+ struct tree *common,
+ struct tree *head,
+ struct tree *merge)
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s -m %s %s %s\n",
+ getenv("GIT_INDEX_FILE"),
+ update_arg,
+ sha1_to_hex(common->object.sha1),
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+#endif
+ const char *argv[] = {
+ "git-read-tree", NULL, "-m", NULL, NULL, NULL,
+ NULL,
+ };
+ argv[1] = update_arg;
+ argv[3] = sha1_to_hex(common->object.sha1);
+ argv[4] = sha1_to_hex(head->object.sha1);
+ argv[5] = sha1_to_hex(merge->object.sha1);
+ int rc = run_command_v(6, argv);
+ return rc < 0 ? -1: rc;
+}
+
+static int fget_sha1(unsigned char *sha, FILE *, int *ch);
+
+/*
+ * Merge the commits h1 and h2, return the resulting virtual
+ * commit object and a flag indicating the cleaness of the merge.
+ */
+static
+struct merge_result merge(struct node *h1,
+ struct node *h2,
+ const char *branch1Name,
+ const char *branch2Name,
+ struct graph *graph,
+ int callDepth /* =0 */,
+ struct node *ancestor /* =None */)
+{
+ struct merge_result result = { NULL, 0 };
+
+ const char *msg;
+ int msglen;
+ output("Merging:");
+ msg = node_title(h1, &msglen);
+ output("%s %.*s", node_hex_sha1(h1), msglen, msg);
+ msg = node_title(h2, &msglen);
+ output("%s %.*s", node_hex_sha1(h2), msglen, msg);
+ if ( !ancestor && !graph )
+ die("graph is not initialized");
+ struct node_list *ca = NULL;
+ if ( ancestor )
+ node_list_insert(ancestor, &ca);
+ else {
+ struct node_list **pca = &ca;
+ char cmd[100];
+ sprintf(cmd, "git-merge-base --all %s %s",
+ node_hex_sha1(h1),
+ node_hex_sha1(h2));
+ FILE *fp = popen(cmd, "r");
+ while (!feof(fp)) {
+ unsigned char sha1[20];
+ int ch;
+ if (fget_sha1(sha1, fp, &ch) == 0) {
+ struct node *n;
+ n = node_alloc(lookup_commit(sha1));
+ node_list_insert(n, pca);
+ pca = &(*pca)->next;
+ }
+ }
+ pclose(fp);
+ }
+
+ output("found %u common ancestor(s):", node_list_count(ca));
+ struct node_list *x;
+ for_each_node_list(x,ca) {
+ msg = node_title(x->node, &msglen);
+ output("%s %.*s", node_hex_sha1(x->node), msglen, msg);
+ }
+
+ struct node *mergedCA = node_list_shift_node(&ca);
+
+ struct node_list *h;
+ for_each_commit(h,ca) {
+ output_indent = callDepth + 1;
+ result = merge(mergedCA, h->node,
+ "Temporary merge branch 1",
+ "Temporary merge branch 2",
+ graph,
+ callDepth + 1,
+ NULL);
+ mergedCA = result.commit;
+ output_indent = callDepth;
+
+ if ( !mergedCA )
+ die("merge returned no commit");
+ }
+
+ if ( callDepth == 0 ) {
+ setup_index(0);
+ index_only = 0;
+ } else {
+ setup_index(1);
+ git_read_tree(h1->tree);
+ index_only = 1;
+ }
+
+ struct merge_tree_result mtr;
+ mtr = merge_trees(h1->tree, h2->tree,
+ mergedCA->tree, branch1Name, branch2Name);
+
+ if ( !ancestor && (mtr.clean || index_only) ) {
+ result.commit = node_alloc_virtual(mtr.tree, "merged tree");
+ struct node_list *parents = NULL;
+ node_list_insert(h1, &parents);
+ node_list_insert(h2, &parents->next);
+ node_set_parents(result.commit, parents);
+ graph_add_node(graph, result.commit);
+ } else
+ result.commit = NULL;
+
+ result.clean = mtr.clean;
+ return result;
+}
+
+#define READ_TREE_FOUND 2
+typedef int (*read_tree_rt_fn_t)(const char *sha1,
+ const char *base,
+ int baselen,
+ const struct name_entry *entry,
+ void *data);
+
+/* git-ls-tree -r -t <tree> */
+static int read_tree_rt(struct tree *tree,
+ const char *base,
+ int baselen,
+ read_tree_rt_fn_t fn,
+ void *data)
+{
+ struct tree_desc desc;
+ struct name_entry entry;
+
+ if (parse_tree(tree))
+ return -1;
+
+ desc.buf = tree->buffer;
+ desc.size = tree->size;
+
+ while (tree_entry(&desc, &entry)) {
+ int retval = fn(entry.sha1, base, baselen, &entry, data);
+
+ switch (retval) {
+ case READ_TREE_RECURSIVE:
+ break;
+ case 0:
+ continue;
+ default:
+ return retval;
+ }
+ if (S_ISDIR(entry.mode)) {
+#if defined(HAVE_ALLOCA)
+ char *path = alloca(baselen + entry.pathlen + 1);
+#else
+ char *path = xmalloc(baselen + entry.pathlen + 1);
+#endif
+ memcpy(path, base, baselen);
+ memcpy(path + baselen, entry.path, entry.pathlen);
+ path[baselen + entry.pathlen] = '/';
+ retval = read_tree_rt(lookup_tree(entry.sha1),
+ path,
+ baselen + entry.pathlen + 1,
+ fn, data);
+#if !defined(HAVE_ALLOCA)
+ free(path);
+#endif
+ if (retval)
+ return retval;
+ }
+ }
+ return 0;
+}
+
+struct files_and_dirs
+{
+ struct path_list *files;
+ struct path_list *dirs;
+};
+
+static int save_files_dirs(const char *sha1,
+ const char *base,
+ int baselen,
+ const struct name_entry *entry,
+ void *data_)
+{
+ struct files_and_dirs *data = data_;
+ char *path = malloc(baselen + entry->pathlen + 1);
+ memcpy(path, base, baselen);
+ memcpy(path + baselen, entry->path, entry->pathlen);
+ path[baselen + entry->pathlen] = '\0';
+
+ if (S_ISDIR(entry->mode))
+ path_list_insert(path, data->dirs);
+ else
+ path_list_insert(path, data->files);
+ return READ_TREE_RECURSIVE;
+}
+
+static int get_files_dirs(struct tree *tree,
+ struct path_list *files,
+ struct path_list *dirs)
+{
+ struct files_and_dirs data;
+ path_list_clear(files, 1);
+ path_list_clear(dirs, 1);
+ data.files = files;
+ data.dirs = dirs;
+ debug("get_files_dirs ...\n");
+ if ( read_tree_rt(tree, "", 0, save_files_dirs, &data) != 0 ) {
+ debug(" get_files_dirs done (0)\n");
+ return 0;
+ }
+ int n = path_list_count(files) + path_list_count(dirs);
+ debug(" get_files_dirs done (%d)\n", n);
+ return n;
+}
+
+static struct index_entry *index_entry_find(struct index_entry *ents,
+ const char *path)
+{
+ struct index_entry *e;
+ for ( e = ents; e; e = e->next )
+ if ( strcmp(e->path, path) == 0 )
+ break;
+ return e;
+}
+
+static struct index_entry *index_entry_get(struct index_entry **ents,
+ const char *path)
+{
+ struct index_entry *e, **tail = ents;
+ for ( e = *ents; e; e = e->next ) {
+ if ( strcmp(e->path, path) == 0 )
+ return e;
+ tail = &e->next;
+ }
+ e = index_entry_alloc(path);
+ memset(e->stages, 0, sizeof(e->stages));
+ return *tail = e;
+}
+
+struct find_entry
+{
+ const char *path;
+ int pathlen;
+ unsigned char *sha;
+ unsigned *mode;
+};
+
+static int find_entry(const char *sha,
+ const char *base,
+ int baselen,
+ const struct name_entry *entry,
+ void *data_)
+{
+ struct find_entry *data = data_;
+ if (baselen + entry->pathlen == data->pathlen &&
+ memcmp(data->path, base, baselen) == 0 &&
+ memcmp(data->path + baselen, entry->path, entry->pathlen) == 0) {
+ memcpy(data->sha, entry->sha1, 20);
+ *data->mode = entry->mode;
+ return READ_TREE_FOUND;
+ }
+ return READ_TREE_RECURSIVE;
+}
+
+/*
+ * Returns a index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct index_entry *index_entry_from_db(const char *path,
+ struct tree *o,
+ struct tree *a,
+ struct tree *b)
+{
+ struct index_entry *e = index_entry_alloc(path);
+ struct find_entry data;
+ data.path = path;
+ data.pathlen = strlen(path);
+ data.sha = e->stages[1].sha;
+ data.mode = &e->stages[1].mode;
+ if ( read_tree_rt(o, "", 0, find_entry, &data) != READ_TREE_FOUND ) {
+ memcpy(e->stages[1].sha, null_sha1, 20);
+ e->stages[1].mode = 0;
+ }
+ data.sha = e->stages[2].sha;
+ data.mode = &e->stages[2].mode;
+ if ( read_tree_rt(a, "", 0, find_entry, &data) != READ_TREE_FOUND ) {
+ memcpy(e->stages[2].sha, null_sha1, 20);
+ e->stages[2].mode = 0;
+ }
+ data.sha = e->stages[3].sha;
+ data.mode = &e->stages[3].mode;
+ if ( read_tree_rt(b, "", 0, find_entry, &data) != READ_TREE_FOUND ) {
+ memcpy(e->stages[3].sha, null_sha1, 20);
+ e->stages[3].mode = 0;
+ }
+ return e;
+}
+
+static void free_index_entries(struct index_entry **ents)
+{
+ while (*ents) {
+ struct index_entry *next = (*ents)->next;
+ free(*ents);
+ *ents = next;
+ }
+}
+
+static int fget_mode(unsigned *mode, FILE *fp, int *ch)
+{
+ int p;
+ char buf[8];
+ for (p = 0; (*ch = fgetc(fp)) != EOF && p < 6;) {
+ if (*ch == '\x20' || *ch == '\t' || *ch == '\n' || *ch == '\r')
+ break;
+ if (*ch < '0' || *ch > '7')
+ return -1;
+ buf[p++] = *ch;
+ }
+ buf[p] = '\0';
+ *mode = strtoul(buf, 0, 8);
+ return 0;
+}
+
+static int fget_sha1(unsigned char *sha, FILE *fp, int *ch)
+{
+ char buf[40];
+ int p;
+ for (p = 0; (*ch = fgetc(fp)) != EOF && p < 40;) {
+ if (('0' <= *ch && *ch <= '9') ||
+ ('a' <= *ch && *ch <= 'f') ||
+ ('A' <= *ch && *ch <= 'F'))
+ buf[p++] = *ch;
+ else
+ return -1;
+ }
+ if ( p != 40 || get_sha1_hex(buf, sha) == -1 )
+ return -1;
+ return 0;
+}
+
+/*
+ * Create a dictionary mapping file names to CacheEntry objects. The
+ * dictionary contains one entry for every path with a non-zero stage entry.
+ */
+static struct index_entry *get_unmerged()
+{
+ struct index_entry *unmerged = NULL;
+ FILE *fp = popen("git-ls-files -z --unmerged", "r");
+ if ( !fp )
+ return NULL;
+ debug("get_unmerged...\n");
+ int ch;
+ while ( !feof(fp) ) {
+ unsigned mode;
+ unsigned char sha[20];
+ char stage = '0';
+ char path[PATH_MAX];
+ int p;
+ /* mode */
+ if ( fget_mode(&mode, fp, &ch) )
+ goto wait_eol;
+ if ( '\x20' != ch )
+ goto wait_eol;
+ /* SHA1 */
+ if ( fget_sha1(sha, fp, &ch) )
+ goto wait_eol;
+ if ( '\x20' != ch )
+ goto wait_eol;
+ /* stage */
+ if ( (ch = fgetc(fp)) != EOF ) {
+ stage = ch;
+ if ( ch < '1' || ch > '3' )
+ goto wait_eol;
+ }
+ if ( (ch = fgetc(fp)) == EOF || '\t' != ch )
+ goto wait_eol;
+ /* path */
+ for ( p = 0; (ch = fgetc(fp)) != EOF; ++p ) {
+ path[p] = ch;
+ if ( !ch )
+ break;
+ if ( p == sizeof(path) - 1 ) {
+ path[p] = '\0';
+ error("path too long: %s", path);
+ goto wait_eol;
+ }
+ }
+ if ( ch )
+ goto wait_eol;
+ struct index_entry *e = index_entry_get(&unmerged, path);
+ e->stages[stage - '1' + 1].mode = mode;
+ memcpy(e->stages[stage - '1' + 1].sha, sha, 20);
+ continue;
+ wait_eol:
+ while ( (ch = fgetc(fp)) != EOF && ch );
+ }
+ pclose(fp);
+ debug(" get_unmerged done\n");
+ return unmerged;
+}
+
+struct rename_entry
+{
+ struct rename_entry *next;
+
+ unsigned char src_sha[20];
+ unsigned src_mode;
+ struct index_entry *src_entry;
+
+ unsigned char dst_sha[20];
+ unsigned dst_mode;
+ struct index_entry *dst_entry;
+
+ unsigned score:16,
+ processed:1;
+
+ char *src; /* dst + strlen(dst) + 1 */
+ char dst[1];
+};
+
+static struct rename_entry *find_rename_bysrc(struct rename_entry *e,
+ const char *name)
+{
+ while ( e ) {
+ if ( strcmp(e->src, name) == 0 )
+ break;
+ e = e->next;
+ }
+ return e;
+}
+
+static struct rename_entry *find_rename_bydst(struct rename_entry *e,
+ const char *name)
+{
+ while ( e ) {
+ if ( strcmp(e->dst, name) == 0 )
+ break;
+ e = e->next;
+ }
+ return e;
+}
+
+static void free_rename_entries(struct rename_entry **list)
+{
+ while (*list) {
+ struct rename_entry *next = (*list)->next;
+ free(*list);
+ *list = next;
+ }
+}
+
+/*
+ * Get information of all renames which occured between 'oTree' and
+ * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and
+ * 'bTree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either aTree or bTree.
+ */
+static struct rename_entry *get_renames(struct tree *tree,
+ struct tree *oTree,
+ struct tree *aTree,
+ struct tree *bTree,
+ struct index_entry **entries)
+{
+ time_t t = time(0);
+ debug("getRenames ...\n");
+ struct rename_entry *renames = NULL;
+ struct rename_entry **rptr = &renames;
+ struct diff_options opts;
+ diff_setup(&opts);
+ opts.recursive = 1;
+ opts.detect_rename = DIFF_DETECT_RENAME;
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ if (diff_setup_done(&opts) < 0)
+ die("diff setup failed");
+ diff_tree_sha1(oTree->object.sha1, tree->object.sha1, "", &opts);
+ diffcore_std(&opts);
+ int i;
+ for (i = 0; i < diff_queued_diff.nr; ++i) {
+ struct rename_entry *re;
+ struct diff_filepair *pair = diff_queued_diff.queue[i];
+ if (pair->status != 'R')
+ continue;
+ size_t l1 = strlen(pair->one->path);
+ size_t l2 = strlen(pair->two->path);
+ re = xmalloc(sizeof(*re) + l1 + l2 + 2);
+ re->src = re->dst + l2;
+ re->next = NULL;
+ re->processed = 0;
+ re->score = pair->score;
+ memcpy(re->src_sha, pair->one->sha1, 20);
+ memcpy(re->src, pair->one->path, ++l1);
+ re->src_mode = pair->one->mode;
+ memcpy(re->dst_sha, pair->two->sha1, 20);
+ memcpy(re->dst, pair->two->path, ++l2);
+ re->dst_mode = pair->two->mode;
+ // TODO: optimize index_entry_find
+ re->src_entry = index_entry_find(*entries, re->src);
+ if ( !re->src_entry ) {
+ re->src_entry = index_entry_from_db(re->src, oTree, aTree, bTree);
+ re->src_entry->next = *entries;
+ *entries = re->src_entry;
+ }
+ re->dst_entry = index_entry_find(*entries, re->dst);
+ if ( !re->dst_entry ) {
+ re->dst_entry = index_entry_from_db(re->dst, oTree, aTree, bTree);
+ re->dst_entry->next = *entries;
+ *entries = re->dst_entry;
+ }
+ *rptr = re;
+ rptr = &re->next;
+ }
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_flush(&opts);
+ debug(" getRenames done in %ld\n", time(0)-t);
+ die("PROFILING");
+ return renames;
+}
+
+static FILE *git_update_index_pipe()
+{
+ return popen("git-update-index -z --index-info", "w");
+}
+
+static int update_stages(FILE *up_index,
+ const char *path,
+ const unsigned char *osha, unsigned omode,
+ const unsigned char *asha, unsigned amode,
+ const unsigned char *bsha, unsigned bmode,
+ int clear /* =True */)
+{
+ if ( !up_index )
+ return -1;
+ if ( clear ) {
+ fprintf(up_index, "0 %s\t%s", sha1_to_hex(null_sha1), path);
+ fputc('\0', up_index);
+ }
+ if ( omode ) {
+ fprintf(up_index, "0%o %s 1\t%s", omode, sha1_to_hex(osha), path);
+ fputc('\0', up_index);
+ }
+ if ( amode ) {
+ fprintf(up_index, "0%o %s 2\t%s", amode, sha1_to_hex(asha), path);
+ fputc('\0', up_index);
+ }
+ if ( bmode ) {
+ fprintf(up_index, "0%o %s 3\t%s", bmode, sha1_to_hex(bsha), path);
+ fputc('\0', up_index);
+ }
+ return 0;
+}
+
+static int remove_path(const char *name)
+{
+ int ret;
+ char *slash;
+
+ ret = unlink(name);
+ if ( ret )
+ return ret;
+ int len = strlen(name);
+ char *dirs = malloc(len+1);
+ memcpy(dirs, name, len);
+ dirs[len] = '\0';
+ while ( (slash = strrchr(name, '/')) ) {
+ *slash = '\0';
+ len = slash - name;
+ if ( rmdir(name) != 0 )
+ break;
+ }
+ free(dirs);
+ return ret;
+}
+
+static int remove_file(FILE *update_index, int clean, const char *path)
+{
+ int updateCache = index_only || clean;
+ int updateWd = !index_only;
+
+ if ( updateCache ) {
+ if ( !update_index )
+ return -1;
+ fprintf(update_index, "0 %s\t%s", sha1_to_hex(null_sha1), path);
+ fputc('\0', update_index);
+ return 0;
+ }
+ if ( updateWd )
+ {
+ unlink(path);
+ if ( errno != ENOENT || errno != EISDIR )
+ return -1;
+ remove_path(path);
+ }
+ return 0;
+}
+
+static char *unique_path(const char *path, const char *branch)
+{
+ char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+ strcpy(newpath, path);
+ strcat(newpath, "~");
+ char *p = newpath + strlen(newpath);
+ strcpy(p, branch);
+ for ( ; *p; ++p )
+ if ( '/' == *p )
+ *p = '_';
+ int suffix = 0;
+ struct stat st;
+ while ( path_list_has_path(¤tFileSet, newpath) ||
+ path_list_has_path(¤tDirectorySet, newpath) ||
+ lstat(newpath, &st) == 0 ) {
+ sprintf(p, "_%d", suffix++);
+ }
+ path_list_insert(newpath, ¤tFileSet);
+ return newpath;
+}
+
+static int mkdir_p(const char *path, unsigned long mode, int create_last)
+{
+ char *buf = strdup(path);
+ char *p;
+
+ for ( p = buf; *p; ++p ) {
+ if ( *p != '/' )
+ continue;
+ *p = '\0';
+ if (mkdir(buf, mode)) {
+ int e = errno;
+ if ( e == EEXIST ) {
+ struct stat st;
+ if ( !stat(buf, &st) && S_ISDIR(st.st_mode) )
+ goto next; /* ok */
+ errno = e;
+ }
+ free(buf);
+ return -1;
+ }
+ next:
+ *p = '/';
+ }
+ free(buf);
+ if ( create_last && mkdir(path, mode) )
+ return -1;
+ return 0;
+}
+
+/* stolen from builtin-cat-file.c */
+static void flush_buffer(int fd, const char *buf, unsigned long size)
+{
+ while (size > 0) {
+ long ret = xwrite(fd, buf, size);
+ if (ret < 0) {
+ /* Ignore epipe */
+ if (errno == EPIPE)
+ break;
+ die("git-cat-file: %s", strerror(errno));
+ } else if (!ret) {
+ die("git-cat-file: disk full?");
+ }
+ size -= ret;
+ buf += ret;
+ }
+}
+
+static void update_file_flags(FILE *up_index,
+ const unsigned char *sha, unsigned mode,
+ const char *path,
+ int updateCache, int updateWd)
+{
+ if ( index_only )
+ updateWd = 0;
+
+ if ( updateWd ) {
+ char type[20];
+ void *buf;
+ unsigned long size;
+
+ buf = read_sha1_file(sha, type, &size);
+ if (!buf)
+ die("cannot read object %s '%s'", sha1_to_hex(sha), path);
+ if ( strcmp(type, blob_type) != 0 )
+ die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+
+ if ( S_ISREG(mode) ) {
+ if ( mkdir_p(path, 0777, 0 /* don't create last element */) )
+ die("failed to create path %s: %s", path, strerror(errno));
+ unlink(path);
+ if ( mode & 0100 )
+ mode = 0777;
+ else
+ mode = 0666;
+ int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
+ if ( fd < 0 )
+ die("failed to open %s: %s", path, strerror(errno));
+ flush_buffer(fd, buf, size);
+ close(fd);
+ } else if ( S_ISLNK(mode) ) {
+ char *linkTarget = malloc(size + 1);
+ memcpy(linkTarget, buf, size);
+ linkTarget[size] = '\0';
+ mkdir_p(path, 0777, 0);
+ symlink(linkTarget, path);
+ } else
+ die("do not know what to do with %06o %s '%s'",
+ mode, sha1_to_hex(sha), path);
+ }
+ if ( updateCache )
+ {
+ fprintf(up_index, "%06o %s\t%s", mode, sha1_to_hex(sha), path);
+ fputc('\0', up_index);
+ }
+}
+
+static void update_file(FILE *up_index,
+ int clean,
+ const unsigned char *sha,
+ unsigned mode,
+ const char *path)
+{
+ update_file_flags(up_index, sha, mode, path,
+ index_only || clean, !index_only);
+}
+
+/* Low level file merging, update and removal */
+
+struct merge_file_info
+{
+ unsigned char sha[20];
+ unsigned mode;
+ unsigned clean:1,
+ merge:1;
+};
+
+static char *git_unpack_file(const unsigned char *sha1, char *path)
+{
+ void *buf;
+ char type[20];
+ unsigned long size;
+ int fd;
+
+ buf = read_sha1_file(sha1, type, &size);
+ if (!buf || strcmp(type, blob_type))
+ die("unable to read blob object %s", sha1_to_hex(sha1));
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write(fd, buf, size) != size)
+ die("unable to write temp-file");
+ close(fd);
+ return path;
+}
+
+static struct merge_file_info merge_file(const char *oPath,
+ const unsigned char *oSha,
+ unsigned oMode,
+ const char *aPath,
+ const unsigned char *aSha,
+ unsigned aMode,
+ const char *bPath,
+ const unsigned char *bSha,
+ unsigned bMode,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ struct merge_file_info result;
+ result.merge = 0;
+ result.clean = 1;
+
+ if ( (S_IFMT & aMode) != (S_IFMT & bMode) ) {
+ result.clean = 0;
+ if ( S_ISREG(aMode) ) {
+ result.mode = aMode;
+ memcpy(result.sha, aSha, 20);
+ } else {
+ result.mode = bMode;
+ memcpy(result.sha, bSha, 20);
+ }
+ } else {
+ if ( memcmp(aSha, oSha, 20) != 0 && memcmp(bSha, oSha, 20) != 0 )
+ result.merge = 1;
+
+ result.mode = aMode == oMode ? bMode: aMode;
+
+ if ( memcmp(aSha, oSha, 20) == 0 )
+ memcpy(result.sha, bSha, 20);
+ else if ( memcmp(bSha, oSha, 20) == 0 )
+ memcpy(result.sha, aSha, 20);
+ else if ( S_ISREG(aMode) ) {
+
+ int code = 1;
+ char orig[PATH_MAX];
+ char src1[PATH_MAX];
+ char src2[PATH_MAX];
+
+ git_unpack_file(oSha, orig);
+ git_unpack_file(aSha, src1);
+ git_unpack_file(bSha, src2);
+
+ const char *argv[] = {
+ "merge", "-L", NULL, "-L", NULL, "-L", NULL,
+ src1, orig, src2,
+ NULL
+ };
+ char *la, *lb, *lo;
+ argv[2] = la = malloc(strlen(branch1Name) + 2 + strlen(aPath));
+ strcat(strcat(strcpy(la, branch1Name), "/"), aPath);
+ argv[6] = lb = malloc(strlen(branch2Name) + 2 + strlen(bPath));
+ strcat(strcat(strcpy(lb, branch2Name), "/"), bPath);
+ argv[4] = lo = malloc(7 + strlen(oPath));
+ strcat(strcpy(lo, "orig/"), oPath);
+
+#if 0
+ printf("%s %s %s %s %s %s %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9]);
+#endif
+ code = run_command_v(10, argv);
+
+ free(la);
+ free(lb);
+ free(lo);
+ if ( code && code < -256 ) {
+ die("Failed to execute 'merge'. merge(1) is used as the "
+ "file-level merge tool. Is 'merge' in your path?");
+ }
+ char cmd[PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "git-hash-object -t blob -w %s", src1);
+ FILE *hash_object = popen(cmd, "r");
+ if ( !hash_object )
+ die("cannot run git-hash-object: %s", strerror(errno));
+ int ch;
+ if ( fget_sha1(result.sha, hash_object, &ch) )
+ die("invalid output from git-hash-object");
+ pclose(hash_object);
+
+ unlink(orig);
+ unlink(src1);
+ unlink(src2);
+
+ result.clean = WEXITSTATUS(code) == 0;
+ } else {
+ if ( !(S_ISLNK(aMode) || S_ISLNK(bMode)) )
+ die("cannot merge modes?");
+
+ memcpy(result.sha, aSha, 20);
+
+ if ( memcmp(aSha, bSha, 20) != 0 )
+ result.clean = 0;
+ }
+ }
+
+ return result;
+}
+
+static int process_renames(struct rename_entry *renamesA,
+ struct rename_entry *renamesB,
+ const char *branchNameA,
+ const char *branchNameB)
+{
+ debug("processRenames...\n");
+ int cleanMerge = 1;
+
+ struct path_list srcNames = {NULL, 0, 0};
+ const struct rename_entry *sre;
+ char **src;
+
+ for (sre = renamesA; sre; sre = sre->next)
+ path_list_insert(sre->src, &srcNames);
+ for (sre = renamesB; sre; sre = sre->next)
+ path_list_insert(sre->src, &srcNames);
+
+ FILE *fp = git_update_index_pipe();
+ for_each_path(src,&srcNames) {
+ struct rename_entry *renames1, *renames2, *ren1, *ren2;
+ const char *branchName1, *branchName2;
+ ren1 = find_rename_bysrc(renamesA, *src);
+ ren2 = find_rename_bysrc(renamesB, *src);
+ if ( ren1 ) {
+ renames1 = renamesA;
+ renames2 = renamesB;
+ branchName1 = branchNameA;
+ branchName2 = branchNameB;
+ } else {
+ renames1 = renamesB;
+ renames2 = renamesA;
+ branchName1 = branchNameB;
+ branchName2 = branchNameA;
+ struct rename_entry *tmp = ren2;
+ ren2 = ren1;
+ ren1 = tmp;
+ }
+
+ ren1->dst_entry->processed = 1;
+ ren1->src_entry->processed = 1;
+
+ if ( ren1->processed )
+ continue;
+ ren1->processed = 1;
+
+ if ( ren2 ) {
+ /* Renamed in 1 and renamed in 2 */
+ if ( strcmp(ren1->src, ren2->src) != 0 )
+ die("ren1.srcName != ren2.srcName");
+ ren2->dst_entry->processed = 1;
+ ren2->processed = 1;
+ if ( strcmp(ren1->dst, ren2->dst) != 0 ) {
+ output("CONFLICT (rename/rename): "
+ "Rename %s->%s in branch %s "
+ "rename %s->%s in %s",
+ *src, ren1->dst, branchName1,
+ *src, ren2->dst, branchName2);
+ cleanMerge = 0;
+ char *dstName1 = ren1->dst, *dstName2 = ren2->dst;
+ if ( path_list_has_path(¤tDirectorySet, ren1->dst) ) {
+ dstName1 = unique_path(ren1->dst, branchName1);
+ output("%s is a directory in %s adding as %s instead",
+ ren1->dst, branchName2, dstName1);
+ remove_file(fp, 0, ren1->dst);
+ }
+ if ( path_list_has_path(¤tDirectorySet, ren2->dst) ) {
+ dstName2 = unique_path(ren2->dst, branchName2);
+ output("%s is a directory in %s adding as %s instead",
+ ren2->dst, branchName1, dstName2);
+ remove_file(fp, 0, ren2->dst);
+ }
+ update_stages(fp, dstName1,
+ NULL, 0,
+ ren1->dst_sha, ren1->dst_mode,
+ NULL, 0,
+ 1 /* clear */);
+ update_stages(fp, dstName2,
+ NULL, 0,
+ NULL, 0,
+ ren2->dst_sha, ren2->dst_mode,
+ 1 /* clear */);
+ } else {
+ remove_file(fp, 1, ren1->src);
+ struct merge_file_info mfi;
+ mfi = merge_file(ren1->src,
+ ren1->src_sha,
+ ren1->src_mode,
+ ren1->dst,
+ ren1->dst_sha,
+ ren1->dst_mode,
+ ren2->dst,
+ ren2->dst_sha,
+ ren2->dst_mode,
+ branchName1,
+ branchName2);
+ if ( mfi.merge || !mfi.clean )
+ output("Renaming %s->%s", *src, ren1->dst);
+
+ if ( mfi.merge )
+ output("Auto-merging %s", ren1->dst);
+
+ if ( !mfi.clean ) {
+ output("CONFLICT (content): merge conflict in %s",
+ ren1->dst);
+ cleanMerge = 0;
+
+ if ( !index_only )
+ update_stages(fp,
+ ren1->dst,
+ ren1->src_sha, ren1->src_mode,
+ ren1->dst_sha, ren1->dst_mode,
+ ren2->dst_sha, ren2->dst_mode,
+ 1 /* clear */);
+ }
+ update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst);
+ }
+ } else {
+ /* Renamed in 1, maybe changed in 2 */
+ remove_file(fp, 1, ren1->src);
+
+ unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20];
+ unsigned srcModeOtherBranch, dstModeOtherBranch;
+
+ int stage = renamesA == renames1 ? 3: 2;
+
+ memcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20);
+ srcModeOtherBranch = ren1->src_entry->stages[stage].mode;
+
+ memcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20);
+ dstModeOtherBranch = ren1->dst_entry->stages[stage].mode;
+
+ int tryMerge = 0;
+ char *newPath;
+ struct rename_entry *dst2;
+
+ if ( path_list_has_path(¤tDirectorySet, ren1->dst) ) {
+ newPath = unique_path(ren1->dst, branchName1);
+ output("CONFLICT (rename/directory): Rename %s->%s in %s "
+ " directory %s added in %s",
+ ren1->src, ren1->dst, branchName1,
+ ren1->dst, branchName2);
+ output("Renaming %s to %s instead", ren1->src, newPath);
+ cleanMerge = 0;
+ remove_file(fp, 0, ren1->dst);
+ update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath);
+ } else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) {
+ output("CONFLICT (rename/delete): Rename %s->%s in %s "
+ "and deleted in %s",
+ ren1->src, ren1->dst, branchName1,
+ branchName2);
+ cleanMerge = 0;
+ update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, ren1->dst);
+ } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) {
+ newPath = unique_path(ren1->dst, branchName2);
+ output("CONFLICT (rename/add): Rename %s->%s in %s. "
+ "%s added in %s",
+ ren1->src, ren1->dst, branchName1,
+ ren1->dst, branchName2);
+ output("Adding as %s instead", newPath);
+ update_file(fp, 0, dstShaOtherBranch, dstModeOtherBranch, newPath);
+ cleanMerge = 0;
+ tryMerge = 1;
+ } else if ( (dst2 = find_rename_bydst(renames2, ren1->dst)) ) {
+ char *newPath1 = unique_path(ren1->dst, branchName1);
+ char *newPath2 = unique_path(dst2->dst, branchName2);
+ output("CONFLICT (rename/rename): Rename %s->%s in %s. "
+ "Rename %s->%s in %s",
+ ren1->src, ren1->dst, branchName1,
+ dst2->src, dst2->dst, branchName2);
+ output("Renaming %s to %s and %s to %s instead",
+ ren1->src, newPath1, dst2->src, newPath2);
+ remove_file(fp, 0, ren1->dst);
+ update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath1);
+ update_file(fp, 0, dst2->dst_sha, dst2->dst_mode, newPath2);
+ dst2->processed = 1;
+ cleanMerge = 0;
+ } else
+ tryMerge = 1;
+
+ if ( tryMerge ) {
+ char *oname = ren1->src;
+ char *aname = ren1->dst;
+ char *bname = ren1->src;
+ unsigned char osha[20], asha[20], bsha[20];
+ unsigned omode = ren1->src_mode;
+ unsigned amode = ren1->dst_mode;
+ unsigned bmode = srcModeOtherBranch;
+ memcpy(osha, ren1->src_sha, 20);
+ memcpy(asha, ren1->dst_sha, 20);
+ memcpy(bsha, srcShaOtherBranch, 20);
+ const char *aBranch = branchName1;
+ const char *bBranch = branchName2;
+
+ if ( renamesA != renames1 ) {
+ memswp(&aname, &bname, sizeof(aname));
+ memswp(asha, bsha, 20);
+ memswp(&aBranch, &bBranch, sizeof(aBranch));
+ }
+ struct merge_file_info mfi;
+ mfi = merge_file(oname, osha, omode,
+ aname, asha, amode,
+ bname, bsha, bmode,
+ aBranch, bBranch);
+
+ if ( mfi.merge || !mfi.clean )
+ output("Renaming %s => %s", ren1->src, ren1->dst);
+ if ( mfi.merge )
+ output("Auto-merging %s", ren1->dst);
+ if ( !mfi.clean ) {
+ output("CONFLICT (rename/modify): Merge conflict in %s",
+ ren1->dst);
+ cleanMerge = 0;
+
+ if ( !index_only )
+ update_stages(fp,
+ ren1->dst,
+ osha, omode,
+ asha, amode,
+ bsha, bmode,
+ 1 /* clear */);
+ }
+ update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst);
+ }
+ }
+ }
+ path_list_clear(&srcNames, 0);
+ if (pclose(fp)) {
+ die("git update-index --index-info failed");
+ }
+ debug(" processRenames done\n");
+ return cleanMerge;
+}
+
+static unsigned char *has_sha(const unsigned char *sha)
+{
+ return memcmp(sha, null_sha1, 20) == 0 ? NULL: (unsigned char *)sha;
+}
+
+/* Per entry merge function */
+static int process_entry(FILE *up_index,
+ struct index_entry *entry,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ /*
+ printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
+ print_index_entry("\tpath: ", entry);
+ */
+ int cleanMerge = 1;
+ const char *path = entry->path;
+ unsigned char *oSha = has_sha(entry->stages[1].sha);
+ unsigned char *aSha = has_sha(entry->stages[2].sha);
+ unsigned char *bSha = has_sha(entry->stages[3].sha);
+ unsigned oMode = entry->stages[1].mode;
+ unsigned aMode = entry->stages[2].mode;
+ unsigned bMode = entry->stages[3].mode;
+
+ if ( oSha && (!aSha || !bSha) ) {
+ /* Case A: Deleted in one */
+ if ( (!aSha && !bSha) ||
+ (sha_eq(aSha, oSha) && !bSha) ||
+ (!aSha && sha_eq(bSha, oSha)) ) {
+ /* Deleted in both or deleted in one and
+ * unchanged in the other */
+ if ( aSha )
+ output("Removing %s", path);
+ remove_file(up_index, 1, path);
+ } else {
+ /* Deleted in one and changed in the other */
+ cleanMerge = 0;
+ if ( !aSha ) {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch1Name,
+ branch2Name, branch2Name, path);
+ update_file(up_index, 0, bSha, bMode, path);
+ } else {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch2Name,
+ branch1Name, branch1Name, path);
+ update_file(up_index, 0, aSha, aMode, path);
+ }
+ }
+
+ } else if ( (!oSha && aSha && !bSha) ||
+ (!oSha && !aSha && bSha) ) {
+ /* Case B: Added in one. */
+ const char *addBranch;
+ const char *otherBranch;
+ unsigned mode;
+ const unsigned char *sha;
+ const char *conf;
+
+ if ( aSha ) {
+ addBranch = branch1Name;
+ otherBranch = branch2Name;
+ mode = aMode;
+ sha = aSha;
+ conf = "file/directory";
+ } else {
+ addBranch = branch2Name;
+ otherBranch = branch1Name;
+ mode = bMode;
+ sha = bSha;
+ conf = "directory/file";
+ }
+ if ( path_list_has_path(¤tDirectorySet, path) ) {
+ cleanMerge = 0;
+ const char *newPath = unique_path(path, addBranch);
+ output("CONFLICT (%s): There is a directory with name %s in %s. "
+ "Adding %s as %s",
+ conf, path, otherBranch, path, newPath);
+ remove_file(up_index, 0, path);
+ update_file(up_index, 0, sha, mode, newPath);
+ } else {
+ output("Adding %s", path);
+ update_file(up_index, 1, sha, mode, path);
+ }
+ } else if ( !oSha && aSha && bSha ) {
+ /* Case C: Added in both (check for same permissions). */
+ if ( sha_eq(aSha, bSha) ) {
+ if ( aMode != bMode ) {
+ cleanMerge = 0;
+ output("CONFLICT: File %s added identically in both branches, "
+ "but permissions conflict %06o->%06o",
+ path, aMode, bMode);
+ output("CONFLICT: adding with permission: %06o", aMode);
+ update_file(up_index, 0, aSha, aMode, path);
+ } else {
+ /* This case is handled by git-read-tree */
+ assert(0 && "This case must be handled by git-read-tree");
+ }
+ } else {
+ cleanMerge = 0;
+ const char *newPath1 = unique_path(path, branch1Name);
+ const char *newPath2 = unique_path(path, branch2Name);
+ output("CONFLICT (add/add): File %s added non-identically "
+ "in both branches. Adding as %s and %s instead.",
+ path, newPath1, newPath2);
+ remove_file(up_index, 0, path);
+ update_file(up_index, 0, aSha, aMode, newPath1);
+ update_file(up_index, 0, bSha, bMode, newPath2);
+ }
+
+ } else if ( oSha && aSha && bSha ) {
+ /* case D: Modified in both, but differently. */
+ output("Auto-merging %s", path);
+ struct merge_file_info mfi;
+ mfi = merge_file(path, oSha, oMode,
+ path, aSha, aMode,
+ path, bSha, bMode,
+ branch1Name, branch2Name);
+
+ if ( mfi.clean )
+ update_file(up_index, 1, mfi.sha, mfi.mode, path);
+ else {
+ cleanMerge = 0;
+ output("CONFLICT (content): Merge conflict in %s", path);
+
+ if ( index_only )
+ update_file(up_index, 0, mfi.sha, mfi.mode, path);
+ else
+ update_file_flags(up_index, mfi.sha, mfi.mode, path,
+ 0 /* updateCache */,
+ 1 /* updateWd */);
+ }
+ } else
+ die("Fatal merge failure, shouldn't happen.");
+
+ return cleanMerge;
+}
+
+static struct merge_tree_result merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ int code;
+ struct merge_tree_result result = { NULL, 0 };
+ if ( !memcmp(common->object.sha1, merge->object.sha1, 20) ) {
+ output("Already uptodate!");
+ result.tree = head;
+ result.clean = 1;
+ return result;
+ }
+
+ debug("merge_trees ...\n");
+ code = git_merge_trees(index_only ? "-i": "-u", common, head, merge);
+
+ if ( code != 0 )
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+
+ result.tree = git_write_tree();
+
+ if ( !result.tree ) {
+ struct path_list mfiles = {NULL, 0, 0}, mdirs = {NULL, 0, 0};
+
+ get_files_dirs(head, ¤tFileSet, ¤tDirectorySet);
+ get_files_dirs(merge, &mfiles, &mdirs);
+
+ path_list_union_update(¤tFileSet, &mfiles);
+ path_list_union_update(¤tDirectorySet, &mdirs);
+
+ struct index_entry *entries = get_unmerged();
+ struct rename_entry *re_head, *re_merge;
+ re_head = get_renames(head, common, head, merge, &entries);
+ re_merge = get_renames(merge, common, head, merge, &entries);
+ result.clean = process_renames(re_head, re_merge,
+ branch1Name, branch2Name);
+ debug("\tprocessing entries...\n");
+ FILE *fp = git_update_index_pipe();
+ struct index_entry *e;
+ for (e = entries; e; e = e->next) {
+ if (e->processed)
+ continue;
+ if (!process_entry(fp, e, branch1Name, branch2Name))
+ result.clean = 0;
+ }
+ if (pclose(fp))
+ die("updating entry failed in git update-index");
+ if (result.clean || index_only)
+ result.tree = git_write_tree();
+ else
+ result.tree = NULL;
+ debug("\t processing entries done\n");
+ free_rename_entries(&re_merge);
+ free_rename_entries(&re_head);
+ free_index_entries(&entries);
+ } else {
+ result.clean = 1;
+ printf("merging of trees %s and %s resulted in %s\n",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1),
+ sha1_to_hex(result.tree->object.sha1));
+ }
+
+ debug(" merge_trees done\n");
+ return result;
+}
+
+static void collect_nodes(struct node *node, struct node_list **res)
+{
+ node_list_insert(node, res);
+ struct node_list *p;
+ for ( p = node->parents; p; p = p->next )
+ collect_nodes(node, res);
+}
+
+struct node_list *reachable_nodes(struct node *n1, struct node *n2)
+{
+ struct node_list *res = NULL;
+ collect_nodes(n1, &res);
+ collect_nodes(n2, &res);
+ return res;
+}
+
+struct graph *graph_build(struct node_list *commits)
+{
+ struct graph *graph = malloc(sizeof(struct graph));
+ memset(graph->commits, 0, sizeof(graph->commits));
+
+ char cmd[256];
+ strcpy(cmd, "git-rev-list --parents");
+ struct node_list *cp;
+ for_each_node_list(cp,commits) {
+ graph_add_node(graph, cp->node);
+ strcat(cmd, " ");
+ strcat(cmd, node_hex_sha1(cp->node));
+ }
+ assert(strlen(cmd) < sizeof(cmd));
+
+ FILE *fp = popen(cmd, "r");
+ if (!fp)
+ die("%s failed: %s", cmd, strerror(errno));
+ while (!feof(fp)) {
+ unsigned char sha[20];
+ int ch;
+ if (fget_sha1(sha, fp, &ch))
+ break;
+ if (EOF == ch)
+ break;
+ /* a commit */
+ struct node *node = graph_node_bysha(graph, sha);
+ if (!node)
+ {
+ node = node_alloc(lookup_commit(sha));
+ graph_add_node(graph, node);
+ }
+ /* ...and its parents. I assume a parent cannot be mentioned
+ before the children. */
+ struct node_list *parents = NULL;
+ while ('\n' != ch) {
+ if (fget_sha1(sha, fp, &ch)) {
+ die("invalid output from %s, "
+ "sha1 (parents) expected",
+ cmd);
+ break;
+ }
+ if (EOF == ch)
+ break;
+ struct node *pn = graph_node_bysha(graph, sha);
+ if (!pn) {
+ pn = node_alloc(lookup_commit(sha));
+ graph_add_node(graph, pn);
+ }
+ node_list_insert(pn, &parents);
+ }
+ node_set_parents(node, parents);
+ }
+ pclose(fp);
+ return graph;
+}
+
+static int get_sha1_0(const char *ref, unsigned char *sha)
+{
+ size_t n = strlen(ref);
+ char *t = xmalloc(n + 4);
+ memcpy(t, ref, n);
+ strcpy(t + n, "^0");
+ int rc = get_sha1(t, sha);
+ free(t);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char *bases[2];
+ static unsigned bases_count = 0;
+
+ original_index_file = getenv("GIT_INDEX_FILE");
+
+ if (!original_index_file)
+ original_index_file = strdup(git_path("index"));
+
+ temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx"));
+
+ if (argc < 4)
+ die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--"))
+ break;
+ if (bases_count < sizeof(bases)/sizeof(*bases))
+ bases[bases_count++] = argv[i];
+ }
+ if (argc - i != 3) /* "--" "<head>" "<remote>" */
+ die("Not handling anything other than two heads merge.");
+
+ unsigned char sha1[20], sha2[20];
+ const char *branch1, *branch2;
+
+ branch1 = argv[++i];
+ if (get_sha1_0(branch1, sha1) != 0)
+ die("invalid first branch %s", branch1);
+
+ branch2 = argv[++i];
+ if (get_sha1_0(branch2, sha2) != 0)
+ die("invalid second branch %s", branch2);
+
+ printf("Merging %s with %s\n", branch1, branch2);
+
+ struct merge_result result;
+ struct node *h1 = node_alloc(lookup_commit(sha1));
+ struct node *h2 = node_alloc(lookup_commit(sha2));
+
+ if (bases_count == 1) {
+ unsigned char shabase[20];
+ if (get_sha1_0(bases[0], shabase) != 0)
+ die("invalid base commit %s", bases[0]);
+ struct node *ancestor = node_alloc(lookup_commit(shabase));
+ result = merge(h1, h2, branch1, branch2, NULL, 0, ancestor);
+ } else {
+ struct node_list *commits = NULL;
+ node_list_insert(h1, &commits);
+ node_list_insert(h2, &commits->next);
+ debug("building graph...\n");
+ struct graph *graph = graph_build(commits);
+ debug(" building graph...\n");
+ result = merge(h1, h2, branch1, branch2, graph, 0, NULL);
+ }
+ return result.clean ? 0: 1;
+}
+
+/*
+vim: sw=8 noet
+*/
diff --git a/path-list.c b/path-list.c
new file mode 100644
index 0000000..95395ab
--- /dev/null
+++ b/path-list.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include "cache.h"
+#include "path-list.h"
+
+/* if there is no exact match, point to the index where the entry could be
+ * inserted */
+static int get_entry_index(const struct path_list *container, const char *path,
+ int *exact_match)
+{
+ int left = -1, right = container->nr;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ int compare = strcmp(path, container->paths[middle]);
+ if (compare < 0)
+ right = middle;
+ else if (compare > 0)
+ left = middle;
+ else {
+ *exact_match = 1;
+ return middle;
+ }
+ }
+
+ *exact_match = 0;
+ return right;
+}
+
+/* returns -1-index if already exists */
+static int add_entry(struct path_list *container, const char *path)
+{
+ int exact_match;
+ int index = get_entry_index(container, path, &exact_match);
+
+ if (exact_match)
+ return -1 - index;
+
+ if (container->nr + 1 >= container->alloc) {
+ container->alloc += 32;
+ container->paths = realloc(container->paths,
+ container->alloc * sizeof(char *));
+ }
+ if (index < container->nr)
+ memmove(container->paths + index + 1,
+ container->paths + index,
+ (container->nr - index) * sizeof(char *));
+ container->paths[index] = strdup(path);
+ container->nr++;
+
+ return index;
+}
+
+char *path_list_insert(char *path, struct path_list *list)
+{
+ int index = add_entry(list, path);
+
+ if (index < 0)
+ index = 1 - index;
+
+ return list->paths[index];
+}
+
+int path_list_has_path(const struct path_list *list, const char *path)
+{
+ int exact_match;
+ get_entry_index(list, path, &exact_match);
+ return exact_match;
+}
+
+/* in place */
+void path_list_union_update(struct path_list *dst, const struct path_list *src)
+{
+ char **new_paths;
+ int i = 0, j = 0, nr = 0, alloc = dst->nr + dst->nr;
+
+ new_paths = xcalloc(sizeof(char *), alloc);
+
+ while (i < dst->nr || j < src->nr) {
+ char **entry = new_paths + nr++;
+ if (i == dst->nr)
+ *entry = src->paths[j++];
+ else if (j == src->nr)
+ *entry = dst->paths[i++];
+ else {
+ int compare = strcmp(dst->paths[i], src->paths[j]);
+ if (compare > 0)
+ *entry = src->paths[j++];
+ else {
+ *entry = dst->paths[i++];
+ if (!compare)
+ free(src->paths[j++]);
+ }
+ }
+ }
+
+ free(dst->paths);
+ dst->paths = new_paths;
+ dst->nr = nr;
+ dst->alloc = alloc;
+}
+
+void print_path_list(const char *text, const struct path_list *p)
+{
+ int i;
+ if ( text )
+ printf("%s\n", text);
+ for (i = 0; i < p->nr; i++)
+ printf("%s\n", p->paths[i]);
+}
+
diff --git a/path-list.h b/path-list.h
new file mode 100644
index 0000000..a12c7a4
--- /dev/null
+++ b/path-list.h
@@ -0,0 +1,32 @@
+#ifndef _PATH_LIST_H_
+#define _PATH_LIST_H_
+
+struct path_list
+{
+ char **paths;
+ unsigned int nr, alloc;
+};
+
+#define for_each_path(p,list) for ( p = (list)->paths; p != (list)->paths + (list)->nr; p++ )
+
+void print_path_list(const char *text, const struct path_list *p);
+
+#define path_list_count(list) (list)->nr
+
+int path_list_has_path(const struct path_list *list, const char *path);
+void path_list_union_update(struct path_list *dst, const struct path_list *src);
+static inline void path_list_clear(struct path_list *list, int free_paths)
+{
+ if (list->paths) {
+ int i;
+ if (free_paths)
+ for (i = 0; i < list->nr; i++)
+ free(list->paths[i]);
+ free(list->paths);
+ }
+ list->paths = NULL;
+ list->nr = list->alloc = 0;
+}
+char *path_list_insert(char *path, struct path_list *list);
+
+#endif /* _PATH_LIST_H_ */
--
1.4.1.rc1.g17dc
^ permalink raw reply related
* [PATCH 3/4] use detachable filepairs and an attempt to cleanup
From: Alex Riesen @ 2006-06-30 0:27 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Junio C Hamano
---
merge-recursive.c | 295 +++++++++++++++++++++++++++++------------------------
1 files changed, 162 insertions(+), 133 deletions(-)
diff --git a/merge-recursive.c b/merge-recursive.c
index abceb30..9a18e06 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -579,26 +579,17 @@ struct rename_entry
{
struct rename_entry *next;
- unsigned char src_sha[20];
- unsigned src_mode;
+ struct diff_filepair *pair;
struct index_entry *src_entry;
-
- unsigned char dst_sha[20];
- unsigned dst_mode;
struct index_entry *dst_entry;
-
- unsigned score:16,
- processed:1;
-
- char *src; /* dst + strlen(dst) + 1 */
- char dst[1];
+ unsigned processed:1;
};
static struct rename_entry *find_rename_bysrc(struct rename_entry *e,
const char *name)
{
while ( e ) {
- if ( strcmp(e->src, name) == 0 )
+ if ( strcmp(e->pair->one->path, name) == 0 )
break;
e = e->next;
}
@@ -609,7 +600,7 @@ static struct rename_entry *find_rename_
const char *name)
{
while ( e ) {
- if ( strcmp(e->dst, name) == 0 )
+ if ( strcmp(e->pair->two->path, name) == 0 )
break;
e = e->next;
}
@@ -620,6 +611,7 @@ static void free_rename_entries(struct r
{
while (*list) {
struct rename_entry *next = (*list)->next;
+ diff_free_filepair((*list)->pair);
free(*list);
*list = next;
}
@@ -656,29 +648,23 @@ static struct rename_entry *get_renames(
struct diff_filepair *pair = diff_queued_diff.queue[i];
if (pair->status != 'R')
continue;
- size_t l1 = strlen(pair->one->path);
- size_t l2 = strlen(pair->two->path);
- re = xmalloc(sizeof(*re) + l1 + l2 + 2);
- re->src = re->dst + l2;
+ diff_queued_diff.queue[i] = NULL;
+ re = xmalloc(sizeof(*re));
re->next = NULL;
re->processed = 0;
- re->score = pair->score;
- memcpy(re->src_sha, pair->one->sha1, 20);
- memcpy(re->src, pair->one->path, ++l1);
- re->src_mode = pair->one->mode;
- memcpy(re->dst_sha, pair->two->sha1, 20);
- memcpy(re->dst, pair->two->path, ++l2);
- re->dst_mode = pair->two->mode;
+ re->pair = pair;
// TODO: optimize index_entry_find
- re->src_entry = index_entry_find(*entries, re->src);
+ re->src_entry = index_entry_find(*entries, re->pair->one->path);
if ( !re->src_entry ) {
- re->src_entry = index_entry_from_db(re->src, oTree, aTree, bTree);
+ re->src_entry = index_entry_from_db(re->pair->one->path,
+ oTree, aTree, bTree);
re->src_entry->next = *entries;
*entries = re->src_entry;
}
- re->dst_entry = index_entry_find(*entries, re->dst);
+ re->dst_entry = index_entry_find(*entries, re->pair->two->path);
if ( !re->dst_entry ) {
- re->dst_entry = index_entry_from_db(re->dst, oTree, aTree, bTree);
+ re->dst_entry = index_entry_from_db(re->pair->two->path,
+ oTree, aTree, bTree);
re->dst_entry->next = *entries;
*entries = re->dst_entry;
}
@@ -688,7 +674,6 @@ static struct rename_entry *get_renames(
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_flush(&opts);
debug(" getRenames done in %ld\n", time(0)-t);
- die("PROFILING");
return renames;
}
@@ -979,12 +964,9 @@ static struct merge_file_info merge_file
NULL
};
char *la, *lb, *lo;
- argv[2] = la = malloc(strlen(branch1Name) + 2 + strlen(aPath));
- strcat(strcat(strcpy(la, branch1Name), "/"), aPath);
- argv[6] = lb = malloc(strlen(branch2Name) + 2 + strlen(bPath));
- strcat(strcat(strcpy(lb, branch2Name), "/"), bPath);
- argv[4] = lo = malloc(7 + strlen(oPath));
- strcat(strcpy(lo, "orig/"), oPath);
+ argv[2] = la = strdup(mkpath("%s/%s", branch1Name, aPath));
+ argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, bPath));
+ argv[4] = lo = strdup(mkpath("orig/%s", oPath));
#if 0
printf("%s %s %s %s %s %s %s %s %s %s\n",
@@ -1029,6 +1011,75 @@ #endif
return result;
}
+static void conflict_rename_rename(FILE *up_index,
+ struct rename_entry *ren1,
+ const char *branch1,
+ struct rename_entry *ren2,
+ const char *branch2)
+{
+ char *del[2];
+ int delp = 0;
+ const char *ren1_dst = ren1->pair->two->path;
+ const char *ren2_dst = ren2->pair->two->path;
+ const char *dstName1 = ren1_dst;
+ const char *dstName2 = ren2_dst;
+ if (path_list_has_path(¤tDirectorySet, ren1_dst)) {
+ dstName1 = del[delp++] = unique_path(ren1_dst, branch1);
+ output("%s is a directory in %s adding as %s instead",
+ ren1_dst, branch2, dstName1);
+ remove_file(up_index, 0, ren1_dst);
+ }
+ if (path_list_has_path(¤tDirectorySet, ren2_dst)) {
+ dstName2 = del[delp++] = unique_path(ren2_dst, branch2);
+ output("%s is a directory in %s adding as %s instead",
+ ren2_dst, branch1, dstName2);
+ remove_file(up_index, 0, ren2_dst);
+ }
+ update_stages(up_index, dstName1,
+ NULL, 0,
+ ren1->pair->two->sha1, ren1->pair->two->mode,
+ NULL, 0,
+ 1 /* clear */);
+ update_stages(up_index, dstName2,
+ NULL, 0,
+ NULL, 0,
+ ren2->pair->two->sha1, ren2->pair->two->mode,
+ 1 /* clear */);
+ while ( delp-- )
+ free(del[delp]);
+}
+
+static void conflict_rename_dir(FILE *up_index,
+ struct rename_entry *ren1,
+ const char *branch1)
+{
+ char *newPath = unique_path(ren1->pair->two->path, branch1);
+ output("Renaming %s to %s instead", ren1->pair->one->path, newPath);
+ remove_file(up_index, 0, ren1->pair->two->path);
+ update_file(up_index, 0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath);
+ free(newPath);
+}
+
+static void conflict_rename_rename_2(FILE *up_index,
+ struct rename_entry *ren1,
+ const char *branch1,
+ struct rename_entry *ren2,
+ const char *branch2)
+{
+ char *newPath1 = unique_path(ren1->pair->two->path, branch1);
+ char *newPath2 = unique_path(ren2->pair->two->path, branch2);
+ output("Renaming %s to %s and %s to %s instead",
+ ren1->pair->one->path, newPath1,
+ ren2->pair->one->path, newPath2);
+ remove_file(up_index, 0, ren1->pair->two->path);
+ update_file(up_index, 0,
+ ren1->pair->two->sha1, ren1->pair->two->mode, newPath1);
+ update_file(up_index, 0,
+ ren2->pair->two->sha1, ren2->pair->two->mode, newPath2);
+ free(newPath2);
+ free(newPath1);
+}
+
static int process_renames(struct rename_entry *renamesA,
struct rename_entry *renamesB,
const char *branchNameA,
@@ -1042,9 +1093,9 @@ static int process_renames(struct rename
char **src;
for (sre = renamesA; sre; sre = sre->next)
- path_list_insert(sre->src, &srcNames);
+ path_list_insert(sre->pair->one->path, &srcNames);
for (sre = renamesB; sre; sre = sre->next)
- path_list_insert(sre->src, &srcNames);
+ path_list_insert(sre->pair->one->path, &srcNames);
FILE *fp = git_update_index_pipe();
for_each_path(src,&srcNames) {
@@ -1074,80 +1125,66 @@ static int process_renames(struct rename
continue;
ren1->processed = 1;
+ const char *ren1_src = ren1->pair->one->path;
+ const char *ren1_dst = ren1->pair->two->path;
+
if ( ren2 ) {
+ const char *ren2_src = ren2->pair->one->path;
+ const char *ren2_dst = ren2->pair->two->path;
/* Renamed in 1 and renamed in 2 */
- if ( strcmp(ren1->src, ren2->src) != 0 )
- die("ren1.srcName != ren2.srcName");
+ if (strcmp(ren1_src, ren2_src) != 0)
+ die("ren1.src != ren2.src");
ren2->dst_entry->processed = 1;
ren2->processed = 1;
- if ( strcmp(ren1->dst, ren2->dst) != 0 ) {
+ if (strcmp(ren1_dst, ren2_dst) != 0) {
+ cleanMerge = 0;
output("CONFLICT (rename/rename): "
"Rename %s->%s in branch %s "
"rename %s->%s in %s",
- *src, ren1->dst, branchName1,
- *src, ren2->dst, branchName2);
- cleanMerge = 0;
- char *dstName1 = ren1->dst, *dstName2 = ren2->dst;
- if ( path_list_has_path(¤tDirectorySet, ren1->dst) ) {
- dstName1 = unique_path(ren1->dst, branchName1);
- output("%s is a directory in %s adding as %s instead",
- ren1->dst, branchName2, dstName1);
- remove_file(fp, 0, ren1->dst);
- }
- if ( path_list_has_path(¤tDirectorySet, ren2->dst) ) {
- dstName2 = unique_path(ren2->dst, branchName2);
- output("%s is a directory in %s adding as %s instead",
- ren2->dst, branchName1, dstName2);
- remove_file(fp, 0, ren2->dst);
- }
- update_stages(fp, dstName1,
- NULL, 0,
- ren1->dst_sha, ren1->dst_mode,
- NULL, 0,
- 1 /* clear */);
- update_stages(fp, dstName2,
- NULL, 0,
- NULL, 0,
- ren2->dst_sha, ren2->dst_mode,
- 1 /* clear */);
+ *src, ren1_dst, branchName1,
+ *src, ren2_dst, branchName2);
+ conflict_rename_rename(fp, ren1, branchName1, ren2, branchName2);
} else {
- remove_file(fp, 1, ren1->src);
+ remove_file(fp, 1, ren1_src);
struct merge_file_info mfi;
- mfi = merge_file(ren1->src,
- ren1->src_sha,
- ren1->src_mode,
- ren1->dst,
- ren1->dst_sha,
- ren1->dst_mode,
- ren2->dst,
- ren2->dst_sha,
- ren2->dst_mode,
+ mfi = merge_file(ren1_src,
+ ren1->pair->one->sha1,
+ ren1->pair->one->mode,
+ ren1_dst,
+ ren1->pair->two->sha1,
+ ren1->pair->two->mode,
+ ren2_dst,
+ ren2->pair->two->sha1,
+ ren2->pair->two->mode,
branchName1,
branchName2);
if ( mfi.merge || !mfi.clean )
- output("Renaming %s->%s", *src, ren1->dst);
+ output("Renaming %s->%s", *src, ren1_dst);
if ( mfi.merge )
- output("Auto-merging %s", ren1->dst);
+ output("Auto-merging %s", ren1_dst);
if ( !mfi.clean ) {
output("CONFLICT (content): merge conflict in %s",
- ren1->dst);
+ ren1_dst);
cleanMerge = 0;
if ( !index_only )
update_stages(fp,
- ren1->dst,
- ren1->src_sha, ren1->src_mode,
- ren1->dst_sha, ren1->dst_mode,
- ren2->dst_sha, ren2->dst_mode,
- 1 /* clear */);
+ ren1_dst,
+ ren1->pair->one->sha1,
+ ren1->pair->one->mode,
+ ren1->pair->two->sha1,
+ ren1->pair->two->mode,
+ ren2->pair->two->sha1,
+ ren2->pair->two->mode,
+ 1 /* clear */);
}
- update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst);
+ update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
}
} else {
/* Renamed in 1, maybe changed in 2 */
- remove_file(fp, 1, ren1->src);
+ remove_file(fp, 1, ren1_src);
unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20];
unsigned srcModeOtherBranch, dstModeOtherBranch;
@@ -1162,62 +1199,52 @@ static int process_renames(struct rename
int tryMerge = 0;
char *newPath;
- struct rename_entry *dst2;
- if ( path_list_has_path(¤tDirectorySet, ren1->dst) ) {
- newPath = unique_path(ren1->dst, branchName1);
+ if (path_list_has_path(¤tDirectorySet, ren1_dst)) {
+ cleanMerge = 0;
output("CONFLICT (rename/directory): Rename %s->%s in %s "
" directory %s added in %s",
- ren1->src, ren1->dst, branchName1,
- ren1->dst, branchName2);
- output("Renaming %s to %s instead", ren1->src, newPath);
- cleanMerge = 0;
- remove_file(fp, 0, ren1->dst);
- update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath);
+ ren1_src, ren1_dst, branchName1,
+ ren1_dst, branchName2);
+ conflict_rename_dir(fp, ren1, branchName1);
} else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) {
+ cleanMerge = 0;
output("CONFLICT (rename/delete): Rename %s->%s in %s "
"and deleted in %s",
- ren1->src, ren1->dst, branchName1,
+ ren1_src, ren1_dst, branchName1,
branchName2);
- cleanMerge = 0;
- update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, ren1->dst);
+ update_file(fp, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
} else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) {
- newPath = unique_path(ren1->dst, branchName2);
+ cleanMerge = 0;
+ tryMerge = 1;
output("CONFLICT (rename/add): Rename %s->%s in %s. "
"%s added in %s",
- ren1->src, ren1->dst, branchName1,
- ren1->dst, branchName2);
+ ren1_src, ren1_dst, branchName1,
+ ren1_dst, branchName2);
+ newPath = unique_path(ren1_dst, branchName2);
output("Adding as %s instead", newPath);
update_file(fp, 0, dstShaOtherBranch, dstModeOtherBranch, newPath);
+ } else if ( (ren2 = find_rename_bydst(renames2, ren1_dst)) ) {
cleanMerge = 0;
- tryMerge = 1;
- } else if ( (dst2 = find_rename_bydst(renames2, ren1->dst)) ) {
- char *newPath1 = unique_path(ren1->dst, branchName1);
- char *newPath2 = unique_path(dst2->dst, branchName2);
+ ren2->processed = 1;
output("CONFLICT (rename/rename): Rename %s->%s in %s. "
"Rename %s->%s in %s",
- ren1->src, ren1->dst, branchName1,
- dst2->src, dst2->dst, branchName2);
- output("Renaming %s to %s and %s to %s instead",
- ren1->src, newPath1, dst2->src, newPath2);
- remove_file(fp, 0, ren1->dst);
- update_file(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath1);
- update_file(fp, 0, dst2->dst_sha, dst2->dst_mode, newPath2);
- dst2->processed = 1;
- cleanMerge = 0;
+ ren1_src, ren1_dst, branchName1,
+ ren2->pair->one->path, ren2->pair->two->path, branchName2);
+ conflict_rename_rename_2(fp, ren1, branchName1, ren2, branchName2);
} else
tryMerge = 1;
if ( tryMerge ) {
- char *oname = ren1->src;
- char *aname = ren1->dst;
- char *bname = ren1->src;
+ const char *oname = ren1_src;
+ const char *aname = ren1_dst;
+ const char *bname = ren1_src;
unsigned char osha[20], asha[20], bsha[20];
- unsigned omode = ren1->src_mode;
- unsigned amode = ren1->dst_mode;
+ unsigned omode = ren1->pair->one->mode;
+ unsigned amode = ren1->pair->two->mode;
unsigned bmode = srcModeOtherBranch;
- memcpy(osha, ren1->src_sha, 20);
- memcpy(asha, ren1->dst_sha, 20);
+ memcpy(osha, ren1->pair->one->sha1, 20);
+ memcpy(asha, ren1->pair->two->sha1, 20);
memcpy(bsha, srcShaOtherBranch, 20);
const char *aBranch = branchName1;
const char *bBranch = branchName2;
@@ -1234,23 +1261,23 @@ static int process_renames(struct rename
aBranch, bBranch);
if ( mfi.merge || !mfi.clean )
- output("Renaming %s => %s", ren1->src, ren1->dst);
+ output("Renaming %s => %s", ren1_src, ren1_dst);
if ( mfi.merge )
- output("Auto-merging %s", ren1->dst);
+ output("Auto-merging %s", ren1_dst);
if ( !mfi.clean ) {
output("CONFLICT (rename/modify): Merge conflict in %s",
- ren1->dst);
+ ren1_dst);
cleanMerge = 0;
if ( !index_only )
update_stages(fp,
- ren1->dst,
- osha, omode,
- asha, amode,
- bsha, bmode,
- 1 /* clear */);
+ ren1_dst,
+ osha, omode,
+ asha, amode,
+ bsha, bmode,
+ 1 /* clear */);
}
- update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst);
+ update_file(fp, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
}
}
}
@@ -1451,16 +1478,18 @@ static struct merge_tree_result merge_tr
if (!process_entry(fp, e, branch1Name, branch2Name))
result.clean = 0;
}
+ free_rename_entries(&re_merge);
+ free_rename_entries(&re_head);
+ free_index_entries(&entries);
+
if (pclose(fp))
die("updating entry failed in git update-index");
+
if (result.clean || index_only)
result.tree = git_write_tree();
else
result.tree = NULL;
debug("\t processing entries done\n");
- free_rename_entries(&re_merge);
- free_rename_entries(&re_head);
- free_index_entries(&entries);
} else {
result.clean = 1;
printf("merging of trees %s and %s resulted in %s\n",
--
1.4.1.rc1.g17dc
^ permalink raw reply related
* [PATCH 4/4] save another call to git-update-index
From: Alex Riesen @ 2006-06-30 0:27 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Junio C Hamano
and a small cleanup
---
merge-recursive.c | 41 ++++++++++++++++++-----------------------
1 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/merge-recursive.c b/merge-recursive.c
index 9a18e06..f3f8e7d 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -21,7 +21,7 @@ #include "graph.h"
#include "path-list.h"
#define HAVE_ALLOCA
-#define DEBUG
+/*#define DEBUG*/
#ifdef DEBUG
#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)
@@ -629,8 +629,10 @@ static struct rename_entry *get_renames(
struct tree *bTree,
struct index_entry **entries)
{
+#ifdef DEBUG
time_t t = time(0);
debug("getRenames ...\n");
+#endif
struct rename_entry *renames = NULL;
struct rename_entry **rptr = &renames;
struct diff_options opts;
@@ -691,22 +693,16 @@ static int update_stages(FILE *up_index,
{
if ( !up_index )
return -1;
- if ( clear ) {
+ if ( clear )
fprintf(up_index, "0 %s\t%s", sha1_to_hex(null_sha1), path);
- fputc('\0', up_index);
- }
- if ( omode ) {
+ if ( omode )
fprintf(up_index, "0%o %s 1\t%s", omode, sha1_to_hex(osha), path);
- fputc('\0', up_index);
- }
- if ( amode ) {
+ if ( amode )
fprintf(up_index, "0%o %s 2\t%s", amode, sha1_to_hex(asha), path);
- fputc('\0', up_index);
- }
- if ( bmode ) {
+ if ( bmode )
fprintf(up_index, "0%o %s 3\t%s", bmode, sha1_to_hex(bsha), path);
+ if ( clear || omode || amode || bmode )
fputc('\0', up_index);
- }
return 0;
}
@@ -1080,7 +1076,8 @@ static void conflict_rename_rename_2(FIL
free(newPath1);
}
-static int process_renames(struct rename_entry *renamesA,
+static int process_renames(FILE* fp,
+ struct rename_entry *renamesA,
struct rename_entry *renamesB,
const char *branchNameA,
const char *branchNameB)
@@ -1097,7 +1094,6 @@ static int process_renames(struct rename
for (sre = renamesB; sre; sre = sre->next)
path_list_insert(sre->pair->one->path, &srcNames);
- FILE *fp = git_update_index_pipe();
for_each_path(src,&srcNames) {
struct rename_entry *renames1, *renames2, *ren1, *ren2;
const char *branchName1, *branchName2;
@@ -1282,9 +1278,6 @@ static int process_renames(struct rename
}
}
path_list_clear(&srcNames, 0);
- if (pclose(fp)) {
- die("git update-index --index-info failed");
- }
debug(" processRenames done\n");
return cleanMerge;
}
@@ -1467,24 +1460,26 @@ static struct merge_tree_result merge_tr
struct rename_entry *re_head, *re_merge;
re_head = get_renames(head, common, head, merge, &entries);
re_merge = get_renames(merge, common, head, merge, &entries);
- result.clean = process_renames(re_head, re_merge,
+ FILE *up_index = git_update_index_pipe();
+ result.clean = process_renames(up_index,
+ re_head, re_merge,
branch1Name, branch2Name);
debug("\tprocessing entries...\n");
- FILE *fp = git_update_index_pipe();
struct index_entry *e;
for (e = entries; e; e = e->next) {
if (e->processed)
continue;
- if (!process_entry(fp, e, branch1Name, branch2Name))
+ if (!process_entry(up_index, e,
+ branch1Name, branch2Name))
result.clean = 0;
}
+ if (pclose(up_index))
+ die("updating entry failed in git update-index");
+
free_rename_entries(&re_merge);
free_rename_entries(&re_head);
free_index_entries(&entries);
- if (pclose(fp))
- die("updating entry failed in git update-index");
-
if (result.clean || index_only)
result.tree = git_write_tree();
else
--
1.4.1.rc1.g17dc
^ permalink raw reply related
* Re: [PATCH] Makefile: set USE_PIC on Linux x86_64 for linking with Git.pm
From: Jakub Narebski @ 2006-06-30 0:29 UTC (permalink / raw)
To: Pavel Roskin, git
In-Reply-To: <1151625780.10358.13.camel@dv>
Pavel Roskin write:
> I don't have the newsgroup access, so I'm copying to the mailing list
> instead.
Newsgroup should be tied to mailing list, so messages on newsgroup
would appear on mailing list and vice versa. Patches were sent to
mailing list.
>> I'm trying to do inobtrusive _optional_ autoconf support in the patch series
>> beginning with
>> Message-ID: <200606290301.51657.jnareb@gmail.com>
>> http://permalink.gmane.org/gmane.comp.version-control.git/22832
>
> The problem with optional support is that you suddenly have two
> alternative mechanisms to adjust the build to the system, and both
> should be kept in a working condition. But it's a good first step.
The idea I started working with autoconf was to be able to say
%configure in .spec file. Then I thought that it would be nice
to have --without-expat, --with-curl=PATH etc. options. Then...
Additionally, optional support would be easier to accept I think.
[repeated]
>> Message-ID: <200606290301.51657.jnareb@gmail.com>
>> http://permalink.gmane.org/gmane.comp.version-control.git/22832
>
> The link doesn't show the "@" characters correctly. Maybe somebody
> could establish a git repository? Ideally, the autoconf changes should
> go to one of the Git branches.
Try yet another git mailing list archive:
http://www.gelato.unsw.edu.au/archives/git/0606/23225.html
I'll try to publish changes at
http://front.fuw.edu.pl/jnareb/scm/git.git/
http://front.fuw.edu.pl/cgi-bin/jnareb/gitweb.cgi?p=git.git
after I learn how to setup ssh proxy or equivalent...
--
Jakub Narebski
Poland
^ permalink raw reply
* [PATCH 7] autoconf: configure.ac uses variables to set in, out and temp files
From: Jakub Narebski @ 2006-06-30 0:11 UTC (permalink / raw)
To: git
In-Reply-To: <200606291947.50980.jnareb@gmail.com>
configure.ac now uses variables to set input file (containing strings
substituted by ./configure script), temporary file (for appending
lines to output file) and output file (with the result configuration).
This is preparation to using other file than config.mak for output.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
configure.ac | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/configure.ac b/configure.ac
index f48307c..58ec57a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,14 +6,18 @@ AC_INIT([git], [1.4.0], [git@vger.kernel
AC_CONFIG_SRCDIR([git.c])
-echo "# config.mak.append. Generated by configure." >> config.mak.append
+config_file=config.mak
+config_append=config.mak.append
+config_in=config.mak.in
+
+echo "# ${config_append}. Generated by configure." >> "${config_append}"
# Definitions of macros
# MY_APPEND_LINE(LINE)
# --------------------
-# Append LINE to file config.mak.append
+# Append LINE to file ${config_append}
AC_DEFUN([MY_APPEND_LINE],
-[[echo "$1" >> config.mak.append]])# MY_APPEND_LINE
+[[echo "$1" >> "${config_append}"]])# MY_APPEND_LINE
# Checks for libraries.
@@ -46,6 +50,6 @@ AC_CHECK_FUNC(setenv,,MY_APPEND_LINE(NO_
# Output files
-AC_CONFIG_FILES([config.mak:config.mak.in:config.mak.append],
-[rm -f config.mak.append])
+AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"],
+[rm -f "${config_append}"])
AC_OUTPUT
--
1.4.0
^ permalink raw reply related
* [PATCH 8] autoconf: ./configure script outputs to config.mac.auto
From: Jakub Narebski @ 2006-06-30 0:32 UTC (permalink / raw)
To: git
In-Reply-To: <200606300211.21922.jnareb@gmail.com>
---
So now to actually use ./configure output you either have to add
-include config.mak.auto
to Makefile before including config.mak, link config.mak.auto to config.mak,
or rename (move) config.mak.auto to config.mak after done with ./configure
configure.ac | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/configure.ac b/configure.ac
index 58ec57a..799bc87 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ AC_INIT([git], [1.4.0], [git@vger.kernel
AC_CONFIG_SRCDIR([git.c])
-config_file=config.mak
+config_file=config.mak.auto
config_append=config.mak.append
config_in=config.mak.in
--
1.4.0
^ permalink raw reply related
* Re: [PATCH 4/4] save another call to git-update-index
From: Johannes Schindelin @ 2006-06-30 0:37 UTC (permalink / raw)
To: Alex Riesen; +Cc: git
In-Reply-To: <20060630002756.GD22618@steel.home>
Hi,
FYI I've been just battling this pipe for a couple of hours. The first
steps were easy, but since I wanted to do it incrementally, the index has
to be written every so often, and I seem not to be able to get that right.
But now it's time to go to bed, so this will wait for tomorrow.
Ciao,
Dscho
^ permalink raw reply
* Re: rebasing trouble
From: Junio C Hamano @ 2006-06-30 1:12 UTC (permalink / raw)
To: J. Bruce Fields; +Cc: git
In-Reply-To: <20060629234502.GB10041@fieldses.org>
"J. Bruce Fields" <bfields@fieldses.org> writes:
> I don't know why I find that syntax so counterintuitive. Something like
>
> git checkout -b newgood master
> git cherry-pick next..good
>
> would seem more obvious.
Yes. Everybody loves to hate this syntax.
^ permalink raw reply
* Re: [PATCH 2/4] make filepairs detachable
From: Junio C Hamano @ 2006-06-30 1:16 UTC (permalink / raw)
To: Alex Riesen; +Cc: git, Johannes Schindelin
In-Reply-To: <20060630002736.GB22618@steel.home>
fork0@t-online.de (Alex Riesen) writes:
> Actually, just make sure diff_flush does not crash for diff queue
> entries which were cleared.
Somehow I really feel uneasy about this one. I think it is the
responsibility of the one who mangles the queue to adjust the
entries and count consistent.
^ permalink raw reply
* Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Michal Ludvig @ 2006-06-30 1:18 UTC (permalink / raw)
To: Herbert Xu; +Cc: linux-crypto, git
Hi Herbert and others,
just a quick question: how can I create a patch with all changes in
cryptodev-2.6 tree against tag v2.6.16 in Linus tree? I've got
git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
cloned here and want to extract all your commits in this tree since
2.6.16. Is there a way to do it in Git/Cogito?
I was playing with cg-mkpatch but that gave me only the last commit
(Twofish updates). I'm sure I could search for your commits in the log
and create those patches one by one but that doesn't seem to be too
efficient. Also tried "cg-mkpatch -r 6246b61[...].." which is your merge
of v2.6.17-rc1 into cryptodev-2.6 but that extracted _all_ commits since
that tag, not only those in your tree.
Any help appreciated :-)
Thanks
Michal
^ permalink raw reply
* GIt.xs merge status
From: Junio C Hamano @ 2006-06-30 1:28 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
In-Reply-To: <7vhd2atid1.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
> Pasky, can we first iron out kinks in the build procedure and
> installation before converting existing programs further? The
> things I worry about currently are:
>
> - the SITELIBARCH gotcha I sent you a message about (and you
> responded to it already -- was that an Acked-by?);
I think this finally was cleared thanks to Pavel Roskin (and an
unrelated workaround at the tip of "pu" by me).
> - RPM target -- we probably acquired a new build-dependency in
> which case the .spec file needs to be updated;
I am having trouble with this. I'd appreciate help from RPM
savvy people (the build failure log at the end).
> - Make sure Git.xs builds and installed result works fine on
> all platforms we care about, including Cygwin and other
> non-Linux boxes.
Help wanted from the list here. One thing to note is that I
understand Pasky is working on not using Devel::PPPort to make
the code work with 5.6, so if your build dies with problems in
that area you might want to wait until that is fixed.
> I'd even suggest we revert the changes to git-fmt-merge-msg to
> keep it working for now, until the above worries are resolved.
> Otherwise we cannot have it in "next" safely (and I REALLY _do_
> want to have Git.pm infrastructure in "next" soonish).
I am still undecided about the reverting, since in the places I
personally care about the program works ;-).
I test-built the tip of "pu" 57628f0e; the RPM build log ends
like this:
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /bin/sh git-core = 1.4.1.rc2.g5762-1 tk >= 8.4
Processing files: git-debuginfo-1.4.1.rc2.g5762-1
Provides: Git.so.debug()(64bit)
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/git-1.4.1.rc2.g5762-1-root-junio
error: Installed (but unpackaged) file(s) found:
/usr/lib64/perl5/5.8.6/x86_64-linux-thread-multi/perllocal.pod
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Error.pm
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Git.pm
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/.packlist
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.bs
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.so
RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/lib64/perl5/5.8.6/x86_64-linux-thread-multi/perllocal.pod
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Error.pm
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Git.pm
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/.packlist
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.bs
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.so
make: *** [rpm] Error 1
^ permalink raw reply
* Re: Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Herbert Xu @ 2006-06-30 1:36 UTC (permalink / raw)
To: Michal Ludvig; +Cc: linux-crypto, git
In-Reply-To: <44A47BE0.9060504@logix.cz>
On Fri, Jun 30, 2006 at 01:18:24PM +1200, Michal Ludvig wrote:
>
> just a quick question: how can I create a patch with all changes in
> cryptodev-2.6 tree against tag v2.6.16 in Linus tree? I've got
> git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
> cloned here and want to extract all your commits in this tree since
> 2.6.16. Is there a way to do it in Git/Cogito?
OK, it's easier if you break this into three problems.
You start by getting all the changes merged right after 2.6.16. This
can be done by locating the merge changeset in Linus's tree. It looks
like this:
commit ec1248e70edc5cf7b485efcc7b41e44e10f422e5
Merge: 3d1f337b3e7378923c89f37afb573a918ef40be5 55e9dce37ddf3ab358ba1d1e9eef4ee4bd8174a6
Author: Linus Torvalds <torvalds@g5.osdl.org>
Date: Tue Mar 21 09:33:19 2006 -0800
Merge master.kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6:
[CRYPTO] aes: Fixed array boundary violation
[CRYPTO] tcrypt: Fix key alignment
[CRYPTO] all: Add missing cra_alignmask
[CRYPTO] all: Use kzalloc where possible
[CRYPTO] api: Align tfm context as wide as possible
[CRYPTO] twofish: Use rol32/ror32 where appropriate
You can get the diff for this merge by doing
$ git-diff-tree -p 3d1f337b3e7378923c89f37afb573a918ef40be5 ec1248e70edc5cf7b485efcc7b41e44e10f422e5
You then repeat this for 2.6.17 where the merge changeset is:
commit 972d19e837833b93466c6f6a8ef2a7d653000aa3
Merge: cdf4f383a4b0ffbf458f65380ecffbeee1f79841 b9d0a25a484a90c1d60b974d115eff2fe580ce16
Author: Linus Torvalds <torvalds@g5.osdl.org>
Date: Mon Jun 26 11:03:29 2006 -0700
Finally you get the changes since that merge by find the common changeset
between cryptodev-2.6 and Linus's tree and getting all the changes after
that.
This method works because all crypto changes since 2.6.16 were merged
from my tree. If this wasn't the case, then you'd have to search
through the history since 2.6.16 to identify the crypto patches that
were merged from other sources.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: Prepare "git-merge-tree" for future work
From: Junio C Hamano @ 2006-06-30 2:04 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0606281401540.12404@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> I punted on trying to use the proper git diff interfaces (they are very
> tightly tied into the "diff_filespec" model - Junio, it might be nice if
> there was some way to use them in a setting where that isn't necessarily
> as natural).
I am not quite sure what you mean. You have a sets of
filepairs, each of which is a pair of filespec, each of which
describes the blob. And diff is about comparing them, possibly
after running rename detection, pickaxe filtering and such. If
the merge-tree's internal representation for each blob is more
efficient or easier to use we could switch to use it as an
improved implementation of filespec but I do not think that is
what you meant, because I suspect that approach does not break
diff from the "model". Care to elaborate a bit more please?
^ permalink raw reply
* Re: Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Linus Torvalds @ 2006-06-30 2:25 UTC (permalink / raw)
To: Herbert Xu; +Cc: Michal Ludvig, linux-crypto, git
In-Reply-To: <20060630013627.GA27527@gondor.apana.org.au>
On Fri, 30 Jun 2006, Herbert Xu wrote:
>
> On Fri, Jun 30, 2006 at 01:18:24PM +1200, Michal Ludvig wrote:
> >
> > just a quick question: how can I create a patch with all changes in
> > cryptodev-2.6 tree against tag v2.6.16 in Linus tree? I've got
> > git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
> > cloned here and want to extract all your commits in this tree since
> > 2.6.16. Is there a way to do it in Git/Cogito?
>
> OK, it's easier if you break this into three problems.
>
> You start by getting all the changes merged right after 2.6.16. This
> can be done by locating the merge changeset in Linus's tree. It looks
> like this:
Actually, there are certainly other, potentially easier, ways to do this.
It depends a bit on what Michal wants, though. Since the current trees
(both mine and the cryptodev tree) have been merging things back and
forth, it's _not_ as easy as just saying "pick all commits that exist in
one branch but not the other", but depending on what Michal wants to do,
git gives other ways to prune out just the info he wants.
The easiest by far is if you only care about a certain sub-directory.
Then, assuming the branch "crypto" is the top-most commit of the cryptodev
repo, just do
git diff v2.6.16..crypto -- crypto/
and that will give you a diff of all the changes since v2.6.16 inside that
subdirectory. That may or may not be sufficient and what Michal wants.
Now, the cryptodev-2.6.git tree doesn't even contain the v2.6.16 tags, but
you can fix that by just doing
git fetch --tags git://git.kernel.org//pub/scm/linux/kernel/git/torvalds/linux-2.6
even if your clone is actually from just the cryptodev-2.6 archive.
Alternatively, if you want to see the individual changes, you can just do
git log -p --full-diff v2.6.16..crypto -- crypto/
which shows you all the commits that changed the crypto/ subdirectory, AND
it shows the other changes those same commits did to other subdirectories
too (which is usually something you want in a case like this).
Finally, what you can also do is that instead of matching for stuff that
changed the crypto/ subdirectory, you could try to match commits where the
committer is somebody special, eg Herbert Xu. We don't have that kind of
thing automated, but here's one way to do it:
git-rev-list --header v2.6.16..crypto |
grep -z 'committer Herbert Xu' |
tr '\0' '\n' |
sed -n '/^[a-f0-9][a-f0-9]*$/p' |
git diff-tree --pretty -p --stdin |
less -S
where the "git-rev-list --header | grep -z" part picks out any commits
committed by Herbert, the "tr '\0' '\n' | sed -n" part then picks up just
the commit ID's from those lines, and the "git-diff-tree" part then shows
those commits as diffs.
(The above should really be quite possible to shorten as
git log -p --committer="Herbert Xu"
but we don't actually support git-rev-list doing matching on
committer/author names - although it should be easy to do in case somebody
wants to have a small git project to get their toes wet, hint hint)
Linus
^ permalink raw reply
* Re: [PATCH] git-grep: --and to combine patterns with and instead of or
From: Matthias Lederhofer @ 2006-06-30 2:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vejx7oa3x.fsf@assigned-by-dhcp.cox.net>
So here is my proposal how extended expressions would work:
extended expressions have three operators:
AND, OR (binary), NOT (unary)
extended expressions do not need an extra option. They will be usable
by adding operators between the expressions; a default operator is
used if no operator is specified. The default operator is by default
OR because currently multiple patterns are combined by OR.
OR and AND have precedence, so there are two possibilities, I'd take
the first one.
1. OR, AND:
This will make it easier to read because OR can be skipped:
pat1 pat2 AND pat3 pat4
= pat1 OR pat2 AND pat3 OR pat4
= (pat1 OR pat2) AND (pat3 OR pat4)
2. AND, OR:
This is a bit more logic if you think of AND as * and OR as +.
Parenthesis may be used to explicitly override the default precedence.
With this setup we can add an option -FOO (I don't now how to call it,
it is the --and from the patch) which changes the default operator and
the precedence. With -FOO you'd get AND as default operator and
precedence AND, OR. Without this option it was easy to write the
formula in a conjungtive form (conjunction of disjunctions), now it is
easy to write a disjunctive form (disjunction of conjunctions):
pat1 pat2 OR pat3 pat4
= pat1 AND pat2 OR pat3 AND pat4
= (pat1 AND pat2) OR (pat3 AND pat4)
With all this as plan for extended expressions we may also introduce
-FOO now with exactly the behaviour of --and in my patch because
currently no explicit operators and parenthesis are allowed, so only
the default operator may be used and -FOO would change the default
operator.
A short example:
(pat1 AND pat2 AND pat3) OR pat4
could be written as
-FOO pat1 pat2 pat3 OR pat4
which is imho quite readable.
So the next problem are names for the options. We would need
- AND: between patterns
- OR: between patterns
- NOT: before a pattern
- FOO: change default operator and precedence
Unfortunately -o, -a, -n are taken and I think the options should be
unique even though they are only allowed at certain positions of the
argument list. I'll think about it a bit, perhaps someone else has a
good idea. FOO should not be named --and imo but I don't have any idea
for a good name atm.
^ permalink raw reply
* Re: Prepare "git-merge-tree" for future work
From: Linus Torvalds @ 2006-06-30 2:32 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vejx7mmaj.fsf@assigned-by-dhcp.cox.net>
On Thu, 29 Jun 2006, Junio C Hamano wrote:
> Linus Torvalds <torvalds@osdl.org> writes:
>
> > I punted on trying to use the proper git diff interfaces (they are very
> > tightly tied into the "diff_filespec" model - Junio, it might be nice if
> > there was some way to use them in a setting where that isn't necessarily
> > as natural).
>
> I am not quite sure what you mean.
For what I wanted to do, I didn't see an easy way to add/populate a new
filespec. It was easier to just use the raw libxdiff interfaces, but
that's really just because I know the interfaces.
In contrast, the ones to diff_filespec I've never really used, and I did
not want to compare blob objects, I very much wanted to compare in-memory
buffers (_and_ potentially blobs).
So if you can show an easy example of how to populate a set of filespec
pairs (not with blobs - with in-memory generated data) and insert them
onto the lists, that would be good.
In fact, maybe you can show me what git-merge-tree needs to do..
Hint hint.
Linus
^ permalink raw reply
* Re: Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Herbert Xu @ 2006-06-30 2:32 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Michal Ludvig, linux-crypto, git
In-Reply-To: <Pine.LNX.4.64.0606291904250.12404@g5.osdl.org>
On Thu, Jun 29, 2006 at 07:25:01PM -0700, Linus Torvalds wrote:
>
> The easiest by far is if you only care about a certain sub-directory.
> Then, assuming the branch "crypto" is the top-most commit of the cryptodev
> repo, just do
>
> git diff v2.6.16..crypto -- crypto/
Yes, you should always do something like this when backporting to make
sure that you haven't missed patches merged in ways that you didn't
anticipate.
Although I'm not aware of any such patches in the time frame that Michal
has in mind.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Linus Torvalds @ 2006-06-30 2:44 UTC (permalink / raw)
To: Herbert Xu; +Cc: Michal Ludvig, linux-crypto, git
In-Reply-To: <20060630023228.GA28850@gondor.apana.org.au>
On Fri, 30 Jun 2006, Herbert Xu wrote:
> On Thu, Jun 29, 2006 at 07:25:01PM -0700, Linus Torvalds wrote:
> >
> > The easiest by far is if you only care about a certain sub-directory.
> > Then, assuming the branch "crypto" is the top-most commit of the cryptodev
> > repo, just do
> >
> > git diff v2.6.16..crypto -- crypto/
>
> Yes, you should always do something like this when backporting to make
> sure that you haven't missed patches merged in ways that you didn't
> anticipate.
>
> Although I'm not aware of any such patches in the time frame that Michal
> has in mind.
Note that this is why
git log -p --full-diff v2.6.16.. crypto/
is so great. It will show everything that touched that subdirectory, along
with the incidentals. I think that in this case, that's exactly what
Michal wants.
Linus
^ permalink raw reply
* Re: Creating diff from 2.6.16 from cryptodev-2.6 git tree
From: Michal Ludvig @ 2006-06-30 3:05 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Herbert Xu, linux-crypto, git
In-Reply-To: <Pine.LNX.4.64.0606291943010.12404@g5.osdl.org>
Linus Torvalds wrote:
> Note that this is why
>
> git log -p --full-diff v2.6.16.. crypto/
>
> is so great. It will show everything that touched that subdirectory, along
> with the incidentals. I think that in this case, that's exactly what
> Michal wants.
Exactly!
I just want to have the latest greatest CryptoAPI on 2.6.16, it's that
simple ;-)
Thanks both for a quick response
Michal
^ permalink raw reply
* [PATCH] consider previous pack undeltified object state only when reusing delta data
From: Nicolas Pitre @ 2006-06-30 3:44 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vwtazobkw.fsf@assigned-by-dhcp.cox.net>
Without this there would never be a chance to improve packing for
previously undeltified objects.
Signed-off-by: Nicolas Pitre <nico@cam.org>
---
On Thu, 29 Jun 2006, Junio C Hamano wrote:
> Nicolas Pitre <nico@cam.org> writes:
>
> > On Thu, 29 Jun 2006, Linus Torvalds wrote:
> >
> >>
> >>
> >> On Thu, 29 Jun 2006, Nicolas Pitre wrote:
> >> >
> >> > On Thu, 29 Jun 2006, Linus Torvalds wrote:
> >> >
> >> > > Instead of having a separate cache, wouldn't it be much better to just
> >> > > take the hint from the previous pack-file?
> >> >
> >> > DOH! ;-)
> >>
> >> Btw, I think this could do with a flag to turn it on/off (but probably
> >> default to on).
> >
> > I think it should simply be coupled with the --no-reuse-delta flag.
>
> I agree that makes sense.
So here it is.
diff --git a/pack-objects.c b/pack-objects.c
index 6e17676..47da33b 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -989,9 +989,10 @@ static int try_delta(struct unpacked *tr
/*
* We do not bother to try a delta that we discarded
- * on an earlier try.
+ * on an earlier try, but only when reusing delta data.
*/
- if (trg_entry->in_pack && trg_entry->in_pack == src_entry->in_pack)
+ if (!no_reuse_delta && trg_entry->in_pack &&
+ trg_entry->in_pack == src_entry->in_pack)
return 0;
/*
^ permalink raw reply related
* Re: [PATCH] git-grep: --and to combine patterns with and instead of or
From: Junio C Hamano @ 2006-06-30 4:13 UTC (permalink / raw)
To: Matthias Lederhofer; +Cc: git
In-Reply-To: <E1Fw8hS-00023y-FY@moooo.ath.cx>
Matthias Lederhofer <matled@gmx.net> writes:
> OR and AND have precedence, so there are two possibilities, I'd take
> the first one.
> 1. OR, AND:
> This will make it easier to read because OR can be skipped:
> pat1 pat2 AND pat3 pat4
> = pat1 OR pat2 AND pat3 OR pat4
> = (pat1 OR pat2) AND (pat3 OR pat4)
> 2. AND, OR:
> This is a bit more logic if you think of AND as * and OR as +.
> ... FOO should not be named --and imo but I don't have any idea
> for a good name atm.
I personally feel FOO should not even exist. An option that
covers the entire expression to make operator precedence in it
sounds quite evil.
I would say make --and bind tighter than --or and use
parentheses as needed. Making --or optional sounds fine as that
would make the default "multiple -e" case similar to what GNU
grep does without any --and nor --or.
^ permalink raw reply
* Abstract out accesses to object hash array
From: Linus Torvalds @ 2006-06-30 4:38 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List
There are a few special places where some programs accessed the object
hash array directly, which bothered me because I wanted to play with some
simple re-organizations.
So this patch makes the object hash array data structures all entirely
local to object.c, and the few users who wanted to look at it now get to
use a function to query how many object index entries there can be, and to
actually access the array.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
It turns out that "lookup_object()" and "memcmp()" are the two hottest
routines by far in git-rev-list, and the problem is that the object array
grows pretty big. On its own, the object array would generally fit pretty
well in the CPU caches, but when it ends up only containing a pointer, and
we then do a memory compare on the result, the CPU cache ends up being
toast.
Now, the object array actually has two bits free, and I was going to hide
two bits of the SHA1 name in there, allowing me to avoid doing a memcmp()
for 75% of the collision cases.
Sadly, my crazy evil scheme doesn't actually help (the hashes work so well
that we have comparatively few collisions), but this cleanup seemed a
worthwhile result of my temporary insanity.
diff --git a/fsck-objects.c b/fsck-objects.c
index 769bb2a..ef54a8a 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -60,12 +60,13 @@ static int objwarning(struct object *obj
static void check_connectivity(void)
{
- int i;
+ int i, max;
/* Look up all the requirements, warn about missing objects.. */
- for (i = 0; i < obj_allocs; i++) {
+ max = get_max_object_index();
+ for (i = 0; i < max; i++) {
const struct object_refs *refs;
- struct object *obj = objs[i];
+ struct object *obj = get_indexed_object(i);
if (!obj)
continue;
diff --git a/name-rev.c b/name-rev.c
index 3a5ac35..6a23f2d 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -234,12 +234,15 @@ #define ishex(x) (isdigit((x)) || ((x) >
fwrite(p_start, p - p_start, 1, stdout);
}
} else if (all) {
- int i;
+ int i, max;
- for (i = 0; i < obj_allocs; i++)
- if (objs[i])
- printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
- get_rev_name(objs[i]));
+ max = get_max_object_index();
+ for (i = 0; i < max; i++) {
+ struct object * obj = get_indexed_object(i);
+ if (!obj)
+ continue;
+ printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj));
+ }
} else {
int i;
for (i = 0; i < revs.nr; i++)
diff --git a/object.c b/object.c
index 37784ce..31c77ea 100644
--- a/object.c
+++ b/object.c
@@ -5,9 +5,18 @@ #include "tree.h"
#include "commit.h"
#include "tag.h"
-struct object **objs;
-static int nr_objs;
-int obj_allocs;
+static struct object **objs;
+static int nr_objs, obj_allocs;
+
+unsigned int get_max_object_index(void)
+{
+ return obj_allocs;
+}
+
+struct object *get_indexed_object(unsigned int idx)
+{
+ return objs[idx];
+}
const char *type_names[] = {
"none", "blob", "tree", "commit", "bad"
diff --git a/object.h b/object.h
index 6f23a9a..e0125e1 100644
--- a/object.h
+++ b/object.h
@@ -40,10 +40,11 @@ struct object {
};
extern int track_object_refs;
-extern int obj_allocs;
-extern struct object **objs;
extern const char *type_names[];
+extern unsigned int get_max_object_index(void);
+extern struct object *get_indexed_object(unsigned int);
+
static inline const char *typename(unsigned int type)
{
return type_names[type > TYPE_TAG ? TYPE_BAD : type];
^ permalink raw reply related
* Re: GIt.xs merge status
From: Pavel Roskin @ 2006-06-30 5:08 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Petr Baudis
In-Reply-To: <7vmzbvmny4.fsf_-_@assigned-by-dhcp.cox.net>
On Thu, 2006-06-29 at 18:28 -0700, Junio C Hamano wrote:
> Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/git-1.4.1.rc2.g5762-1-root-junio
> error: Installed (but unpackaged) file(s) found:
> /usr/lib64/perl5/5.8.6/x86_64-linux-thread-multi/perllocal.pod
> /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Error.pm
> /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/Git.pm
> /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/.packlist
> /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.bs
> /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi/auto/Git/Git.so
I guess all this perl stuff should be in a separate module perl-Git to
comply with Red Hat conventions. This would make git-core depend on
perl-Git, but it's OK.
Error.pm is already provided by perl-Error. If we require perl(Error)
for building, it won't be installed. Actually, probing for Error.pm is
incorrect, so I'm fixing it.
Git.bs, .packlist and perllocal.pod should be removed - that's what
other Perl packages do. Red Hat packages use INSTALLDIRS=vendor so that
"site_perl" becomes "vendor_perl".
While hacking that, I have wound that "--without doc" is broken in pu,
so I'm fixing it as well. The patches will be sent shortly.
--
Regards,
Pavel Roskin
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox