* Re: [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Junio C Hamano @ 2006-07-08 18:25 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0607081844580.29667@wbgn013.biozentrum.uni-wuerzburg.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> in commit 07002287f3e219a16a948a8a6eca0a41162a491f
>> you cleaned up 'replace ugly and unportable sed invocation' as you said.
>> Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
>> and 'rfile'.
>
> Ack. Note that this is yet-another-reason to step away from scripts.
Are you talking about doing this part in the Makefile in C ;-)?
^ permalink raw reply
* Re: [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Johannes Schindelin @ 2006-07-08 16:47 UTC (permalink / raw)
To: Michal Rokos; +Cc: git
In-Reply-To: <200607081727.10837.michal.rokos@nextsoft.cz>
Hi,
On Sat, 8 Jul 2006, Michal Rokos wrote:
> Johannes,
Could you Cc me next time you address me personally? There are times when
I am too busy to read all the mails of all the lists, and your mail would
have been not read by me at those occasions.
> in commit 07002287f3e219a16a948a8a6eca0a41162a491f
> you cleaned up 'replace ugly and unportable sed invocation' as you said.
> Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
> and 'rfile'.
Ack. Note that this is yet-another-reason to step away from scripts.
Ciao,
Dscho
^ permalink raw reply
* [WIP] Status update on merge-recursive in C
From: Johannes Schindelin @ 2006-07-08 16:42 UTC (permalink / raw)
To: git
This is just an update for people being interested. Alex and me were
busy with that project for a few days now. While it has progressed nicely,
there are quite a couple TODOs in merge-recursive.c, just search for "TODO".
For impatient people: yes, it passes all the tests, and yes, according
to the evil test Alex did, it is faster than the Python script.
But no, it is not yet finished. Biggest points are:
- there are still three external calls
- in the end, it should not be necessary to write the index more than once
(just before exiting)
- a lot of things can be refactored to make the code easier and shorter
BTW we cannot just plug in git-merge-tree yet, because git-merge-tree
does not handle renames at all.
This patch is meant for testing, and as such,
- it compile the program to git-merge-recur
- it adjusts the scripts and tests to use git-merge-recur instead of
git-merge-recursive
- it provides "TEST", a script to execute the tests regarding -recursive
- it inlines the changes to read-cache.c (read_cache_from(), discard_cache()
and refresh_cache_entry())
Brought to you by Alex Riesen and Dscho
---
Makefile | 8
TEST | 10
cache.h | 4
git-merge.sh | 6
git-rebase.sh | 4
merge-recursive.c | 1560 +++++++++++++++++++++++++++++++++++++++++++++++
path-list.c | 105 +++
path-list.h | 22 +
read-cache.c | 104 ++-
t/t3402-rebase-merge.sh | 2
10 files changed, 1773 insertions(+), 52 deletions(-)
diff --git a/Makefile b/Makefile
index 8d429a0..11feefe 100644
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,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 \
@@ -647,6 +648,11 @@ git-http-push$X: revision.o http.o http-
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+merge-recursive.o path-list.o: path-list.h
+git-merge-recur$X: merge-recursive.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/TEST b/TEST
new file mode 100755
index 0000000..d530983
--- /dev/null
+++ b/TEST
@@ -0,0 +1,10 @@
+#!/bin/sh -x
+cd t || exit
+./t3400-rebase.sh "$@" && \
+./t6020-merge-df.sh "$@" && \
+./t3401-rebase-partial.sh "$@" && \
+./t6021-merge-criss-cross.sh "$@" && \
+./t3402-rebase-merge.sh "$@" && \
+./t6022-merge-rename.sh "$@" && \
+./t6010-merge-base.sh "$@" && \
+:
diff --git a/cache.h b/cache.h
index 7b5c91c..08777ca 100644
--- a/cache.h
+++ b/cache.h
@@ -115,6 +115,7 @@ #define cache_entry_size(len) ((offsetof
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
extern struct cache_tree *active_cache_tree;
+extern int cache_errno;
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@ -142,13 +143,16 @@ #define alloc_nr(x) (((x)+16)*3/2)
/* Initialize and use the cache information */
extern int read_cache(void);
+extern int read_cache_from(const char *path);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
+extern int discard_cache(void);
extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
extern int add_cache_entry(struct cache_entry *ce, int option);
+extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_cache_entry_at(int pos);
extern int remove_file_from_cache(const char *path);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
diff --git a/git-merge.sh b/git-merge.sh
index 24e3b50..b26ca14 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -9,15 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s
LF='
'
-all_strategies='recursive octopus resolve stupid ours'
-default_twohead_strategies='recursive'
+all_strategies='recur recur octopus resolve stupid ours'
+default_twohead_strategies='recur'
default_octopus_strategies='octopus'
no_trivial_merge_strategies='ours'
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/git-rebase.sh b/git-rebase.sh
index 3945e06..5d4c7d2 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -35,7 +35,7 @@ If you would prefer to skip this patch,
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
unset newbase
-strategy=recursive
+strategy=recur
do_merge=
dotest=$GIT_DIR/.dotest-merge
prec=4
@@ -292,7 +292,7 @@ then
exit $?
fi
-if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
+if test "@@NO_PYTHON@@" && test "$strategy" = "recur"
then
die 'The recursive merge strategy currently relies on Python,
which this installation of git was not configured with. Please consider
diff --git a/merge-recursive.c b/merge-recursive.c
new file mode 100644
index 0000000..07a2b38
--- /dev/null
+++ b/merge-recursive.c
@@ -0,0 +1,1560 @@
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
+ */
+#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 "cache-tree.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "run-command.h"
+#include "tag.h"
+
+#include "path-list.h"
+
+/*#define DEBUG*/
+
+#ifdef DEBUG
+#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)
+#else
+#define debug(args, ...)
+#endif
+
+#ifdef DEBUG
+#include "quote.h"
+static void show_ce_entry(const char *tag, struct cache_entry *ce)
+{
+ if (tag && *tag &&
+ (ce->ce_flags & htons(CE_VALID))) {
+ static char alttag[4];
+ memcpy(alttag, tag, 3);
+ if (isalpha(tag[0]))
+ alttag[0] = tolower(tag[0]);
+ else if (tag[0] == '?')
+ alttag[0] = '!';
+ else {
+ alttag[0] = 'v';
+ alttag[1] = tag[0];
+ alttag[2] = ' ';
+ alttag[3] = 0;
+ }
+ tag = alttag;
+ }
+
+ fprintf(stderr,"%s%06o %s %d\t",
+ tag,
+ ntohl(ce->ce_mode),
+ sha1_to_hex(ce->sha1),
+ ce_stage(ce));
+ write_name_quoted("", 0, ce->name,
+ '\n', stderr);
+ fputc('\n', stderr);
+}
+
+static void ls_files() {
+ int i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ show_ce_entry("", ce);
+ }
+ fprintf(stderr, "---\n");
+}
+#endif
+
+/*
+ * A virtual commit has
+ * - (const char *)commit->util set to the name, and
+ * - *(int *)commit->object.sha1 set to the virtual id.
+ */
+static const char *commit_title(struct commit *commit, int *len)
+{
+ const char *s = "(null commit)";
+ *len = strlen(s);
+
+ if ( commit->util ) {
+ s = commit->util;
+ *len = strlen(s);
+ } else {
+ if ( parse_commit(commit) != 0 ) {
+ s = "(bad commit)";
+ *len = strlen(s);
+ } else {
+ s = 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;
+}
+
+static const char *commit_hex_sha1(const struct commit *commit)
+{
+ return commit->util ? "virtual" : commit ?
+ sha1_to_hex(commit->object.sha1) : "undefined";
+}
+
+static unsigned commit_list_count(const struct commit_list *l)
+{
+ unsigned c = 0;
+ for (; l; l = l->next )
+ c++;
+ return c;
+}
+
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+{
+ struct commit *commit = xcalloc(1, sizeof(struct commit));
+ static unsigned virtual_id = 1;
+ commit->tree = tree;
+ commit->util = (void*)comment;
+ *(int*)commit->object.sha1 = virtual_id++;
+ return commit;
+}
+
+/*
+ * TODO: we should not have to copy the SHA1s around, but rather reference
+ * them. That way, sha_eq() is just sha1 == sha2.
+ */
+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;
+ }
+}
+
+/*
+ * TODO: we should convert the merge_result users to
+ * int blabla(..., struct commit **result)
+ * like everywhere else in git.
+ * Same goes for merge_tree_result and merge_file_info.
+ */
+struct merge_result
+{
+ struct commit *commit;
+ unsigned clean:1;
+};
+
+struct merge_tree_result
+{
+ struct tree *tree;
+ unsigned clean:1;
+};
+
+/*
+ * TODO: check if we can just reuse the active_cache structure: it is already
+ * sorted (by name, stage).
+ * Only problem: do not write it when flushing the cache.
+ */
+struct stage_data
+{
+ struct
+ {
+ unsigned mode;
+ unsigned char sha[20];
+ } stages[4];
+ unsigned processed:1;
+};
+
+static struct path_list currentFileSet = {NULL, 0, 0, 1};
+static struct path_list currentDirectorySet = {NULL, 0, 0, 1};
+
+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 int cache_dirty = 0;
+
+static int flush_cache()
+{
+ /* flush temporary index */
+ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ int fd = hold_lock_file_for_update(lock, getenv("GIT_INDEX_FILE"));
+ if (fd < 0)
+ die("could not lock %s", temporary_index_file);
+ if (write_cache(fd, active_cache, active_nr) ||
+ close(fd) || commit_lock_file(lock))
+ die ("unable to write %s", getenv("GIT_INDEX_FILE"));
+ discard_cache();
+ cache_dirty = 0;
+ return 0;
+}
+
+static void setup_index(int temp)
+{
+ const char *idx = temp ? temporary_index_file: original_index_file;
+ if (cache_dirty)
+ die("fatal: cache changed flush_cache();");
+ unlink(temporary_index_file);
+ setenv("GIT_INDEX_FILE", idx, 1);
+ discard_cache();
+}
+
+static struct cache_entry *make_cache_entry(unsigned int mode,
+ const unsigned char *sha1, const char *path, int stage, int refresh)
+{
+ int size, len;
+ struct cache_entry *ce;
+
+ if (!verify_path(path))
+ return NULL;
+
+ len = strlen(path);
+ size = cache_entry_size(len);
+ ce = xcalloc(1, size);
+
+ memcpy(ce->sha1, sha1, 20);
+ memcpy(ce->name, path, len);
+ ce->ce_flags = create_ce_flags(len, stage);
+ ce->ce_mode = create_ce_mode(mode);
+
+ if (refresh)
+ return refresh_cache_entry(ce, 0);
+
+ return ce;
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+ const char *path, int stage, int refresh, int options)
+{
+ struct cache_entry *ce;
+ if (!cache_dirty)
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+ if (!ce)
+ return error("cache_addinfo failed: %s", strerror(cache_errno));
+ return add_cache_entry(ce, options);
+}
+
+/*
+ * 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;
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-read-tree.c
+ */
+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, };
+ if (cache_dirty)
+ die("read-tree with dirty cache");
+ argv[1] = sha1_to_hex(tree->object.sha1);
+ int rc = run_command_v(2, argv);
+ return rc < 0 ? -1: rc;
+}
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-read-tree.c
+ */
+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,
+ };
+ if (cache_dirty)
+ flush_cache();
+ 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;
+}
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-write-tree.c
+ */
+static struct tree *git_write_tree()
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n",
+ getenv("GIT_INDEX_FILE"));
+#endif
+ if (cache_dirty)
+ flush_cache();
+ 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);
+}
+
+/*
+ * TODO: get rid of files_and_dirs; we do not use it except for
+ * current_file_set and current_dir_set, which are global already.
+ */
+static struct
+{
+ struct path_list *files;
+ struct path_list *dirs;
+} files_and_dirs;
+
+static int save_files_dirs(const unsigned char *sha1,
+ const char *base, int baselen, const char *path,
+ unsigned int mode, int stage)
+{
+ int len = strlen(path);
+ char *newpath = malloc(baselen + len + 1);
+ memcpy(newpath, base, baselen);
+ memcpy(newpath + baselen, path, len);
+ newpath[baselen + len] = '\0';
+
+ if (S_ISDIR(mode))
+ path_list_insert(newpath, files_and_dirs.dirs);
+ else
+ path_list_insert(newpath, files_and_dirs.files);
+ free(newpath);
+
+ return READ_TREE_RECURSIVE;
+}
+
+static int get_files_dirs(struct tree *tree,
+ struct path_list *files,
+ struct path_list *dirs)
+{
+ int n;
+ files_and_dirs.files = files;
+ files_and_dirs.dirs = dirs;
+ debug("get_files_dirs ...\n");
+ if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) {
+ debug(" get_files_dirs done (0)\n");
+ return 0;
+ }
+ n = files->nr + dirs->nr;
+ debug(" get_files_dirs done (%d)\n", n);
+ return n;
+}
+
+/*
+ * TODO: this wrapper is so small, we can use path_list_lookup directly.
+ * Same goes for index_entry_get(), free_index_entries(), find_rename_bysrc(),
+ * free_rename_entries().
+ */
+static struct stage_data *index_entry_find(struct path_list *ents,
+ const char *path)
+{
+ struct path_list_item *item = path_list_lookup(path, ents);
+ if (item)
+ return item->util;
+ return NULL;
+}
+
+static struct stage_data *index_entry_get(struct path_list *ents,
+ const char *path)
+{
+ struct path_list_item *item = path_list_lookup(path, ents);
+
+ if (item == NULL) {
+ item = path_list_insert(path, ents);
+ item->util = xcalloc(1, sizeof(struct stage_data));
+ }
+ return item->util;
+}
+
+/*
+ * TODO: since the result of index_entry_from_db() is tucked into a
+ * path_list anyway, this helper can do that already.
+ */
+/*
+ * Returns a index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct stage_data *index_entry_from_db(const char *path,
+ struct tree *o,
+ struct tree *a,
+ struct tree *b)
+{
+ struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
+ get_tree_entry(o->object.sha1, path,
+ e->stages[1].sha, &e->stages[1].mode);
+ get_tree_entry(a->object.sha1, path,
+ e->stages[2].sha, &e->stages[2].mode);
+ get_tree_entry(b->object.sha1, path,
+ e->stages[3].sha, &e->stages[3].mode);
+ return e;
+}
+
+static void free_index_entries(struct path_list **ents)
+{
+ if (!*ents)
+ return;
+
+ path_list_clear(*ents, 1);
+ free(*ents);
+ *ents = NULL;
+}
+
+/*
+ * 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 path_list *get_unmerged()
+{
+ struct path_list *unmerged = xcalloc(1, sizeof(struct path_list));
+ int i;
+
+ unmerged->strdup_paths = 1;
+ if (!cache_dirty) {
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ }
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+
+ struct stage_data *e = index_entry_get(unmerged, ce->name);
+ e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+ memcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20);
+ }
+
+ debug(" get_unmerged done\n");
+ return unmerged;
+}
+
+struct rename
+{
+ struct diff_filepair *pair;
+ struct stage_data *src_entry;
+ struct stage_data *dst_entry;
+ unsigned processed:1;
+};
+
+static struct rename *find_rename_bysrc(struct path_list *e,
+ const char *name)
+{
+ struct path_list_item *item = path_list_lookup(name, e);
+ if (item)
+ return item->util;
+ return NULL;
+}
+
+static void free_rename_entries(struct path_list **list)
+{
+ if (!*list)
+ return;
+
+ path_list_clear(*list, 0);
+ free(*list);
+ *list = NULL;
+}
+
+/*
+ * 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 path_list *get_renames(struct tree *tree,
+ struct tree *oTree,
+ struct tree *aTree,
+ struct tree *bTree,
+ struct path_list *entries)
+{
+#ifdef DEBUG
+ time_t t = time(0);
+ debug("getRenames ...\n");
+#endif
+ int i;
+ struct path_list *renames = xcalloc(1, sizeof(struct path_list));
+ 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);
+ for (i = 0; i < diff_queued_diff.nr; ++i) {
+ struct rename *re;
+ struct diff_filepair *pair = diff_queued_diff.queue[i];
+ if (pair->status != 'R') {
+ diff_free_filepair(pair);
+ continue;
+ }
+ re = xmalloc(sizeof(*re));
+ re->processed = 0;
+ re->pair = pair;
+ re->src_entry = index_entry_find(entries, re->pair->one->path);
+ /* TODO: should it not be an error, if src_entry was found? */
+ if ( !re->src_entry ) {
+ re->src_entry = index_entry_from_db(re->pair->one->path,
+ oTree, aTree, bTree);
+ struct path_list_item *item =
+ path_list_insert(re->pair->one->path, entries);
+ item->util = re->src_entry;
+ }
+ re->dst_entry = index_entry_find(entries, re->pair->two->path);
+ if ( !re->dst_entry ) {
+ re->dst_entry = index_entry_from_db(re->pair->two->path,
+ oTree, aTree, bTree);
+ struct path_list_item *item =
+ path_list_insert(re->pair->two->path, entries);
+ item->util = re->dst_entry;
+ }
+ struct path_list_item *item = path_list_insert(pair->one->path, renames);
+ item->util = re;
+ }
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_queued_diff.nr = 0;
+ diff_flush(&opts);
+ debug(" getRenames done in %ld\n", time(0)-t);
+ return renames;
+}
+
+/*
+ * TODO: the code would be way nicer, if we had a struct containing just sha1 and mode.
+ * In this particular case, we might get away reusing stage_data, no?
+ */
+int update_stages(const char *path,
+ unsigned char *osha, unsigned omode,
+ unsigned char *asha, unsigned amode,
+ unsigned char *bsha, unsigned bmode,
+ int clear /* =True */)
+{
+ int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+ if ( clear )
+ if (add_cacheinfo(0, null_sha1, path, 0, 0, options))
+ return -1;
+ if ( omode )
+ if (add_cacheinfo(omode, osha, path, 1, 0, options))
+ return -1;
+ if ( amode )
+ if (add_cacheinfo(omode, osha, path, 2, 0, options))
+ return -1;
+ if ( bmode )
+ if (add_cacheinfo(omode, osha, path, 3, 0, options))
+ return -1;
+ return 0;
+}
+
+/*
+ * TODO: there has to be a function in libgit doing this exact thing.
+ */
+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;
+}
+
+/* General TODO: unC99ify the code: no declaration after code */
+/* General TODO: no javaIfiCation: rename updateCache to update_cache */
+/*
+ * TODO: once we no longer call external programs, we'd probably be better of
+ * not setting / getting the environment variable GIT_INDEX_FILE all the time.
+ */
+int remove_file(int clean, const char *path)
+{
+ int updateCache = index_only || clean;
+ int updateWd = !index_only;
+
+ if ( updateCache ) {
+ if (!cache_dirty)
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ if (remove_file_from_cache(path))
+ return -1;
+ }
+ 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;
+}
+
+/*
+ * TODO: except for create_last, this so looks like
+ * safe_create_leading_directories().
+ */
+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;
+}
+
+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("merge-recursive: %s", strerror(errno));
+ } else if (!ret) {
+ die("merge-recursive: disk full?");
+ }
+ size -= ret;
+ buf += ret;
+ }
+}
+
+/* General TODO: reindent according to guide lines (no if ( blabla )) */
+void update_file_flags(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 )
+ add_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD);
+}
+
+/* TODO: is this often used? if not, do direct call */
+void update_file(int clean,
+ const unsigned char *sha,
+ unsigned mode,
+ const char *path)
+{
+ update_file_flags(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");
+ flush_buffer(fd, buf, size);
+ close(fd);
+ return path;
+}
+
+/*
+ * TODO: the signature would be much more efficient using stage_data
+ */
+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 = 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",
+ 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?");
+ }
+ struct stat st;
+ int fd = open(src1, O_RDONLY);
+ if (fd < 0 || fstat(fd, &st) < 0 ||
+ index_fd(result.sha, fd, &st, 1,
+ "blob"))
+ die("Unable to add %s to database", src1);
+ close(fd);
+
+ 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 void conflict_rename_rename(struct rename *ren1,
+ const char *branch1,
+ struct rename *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(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(0, ren2_dst);
+ }
+ update_stages(dstName1,
+ NULL, 0,
+ ren1->pair->two->sha1, ren1->pair->two->mode,
+ NULL, 0,
+ 1 /* clear */);
+ update_stages(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(struct rename *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(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath);
+ free(newPath);
+}
+
+static void conflict_rename_rename_2(struct rename *ren1,
+ const char *branch1,
+ struct rename *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(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath1);
+ update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, newPath2);
+ free(newPath2);
+ free(newPath1);
+}
+
+/* General TODO: get rid of all the debug messages */
+static int process_renames(struct path_list *renamesA,
+ struct path_list *renamesB,
+ const char *branchNameA,
+ const char *branchNameB)
+{
+ int cleanMerge = 1, i;
+ struct path_list srcNames = {NULL, 0, 0, 0}, byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0};
+ const struct rename *sre;
+
+ /*
+ * TODO: think about a saner way to do this.
+ * Since both renamesA and renamesB are sorted, it should
+ * be much more efficient to traverse both simultaneously,
+ * only byDstA and byDstB should be needed.
+ */
+ debug("processRenames...\n");
+ for (i = 0; i < renamesA->nr; i++) {
+ sre = renamesA->items[i].util;
+ path_list_insert(sre->pair->one->path, &srcNames);
+ path_list_insert(sre->pair->two->path, &byDstA)->util
+ = sre->dst_entry;
+ }
+ for (i = 0; i < renamesB->nr; i++) {
+ sre = renamesB->items[i].util;
+ path_list_insert(sre->pair->one->path, &srcNames);
+ path_list_insert(sre->pair->two->path, &byDstB)->util
+ = sre->dst_entry;
+ }
+
+ for (i = 0; i < srcNames.nr; i++) {
+ char *src = srcNames.items[i].path;
+ struct path_list *renames1, *renames2, *renames2Dst;
+ struct rename *ren1, *ren2;
+ const char *branchName1, *branchName2;
+ ren1 = find_rename_bysrc(renamesA, src);
+ ren2 = find_rename_bysrc(renamesB, src);
+ /* TODO: refactor, so that 1/2 are not needed */
+ if ( ren1 ) {
+ renames1 = renamesA;
+ renames2 = renamesB;
+ renames2Dst = &byDstB;
+ branchName1 = branchNameA;
+ branchName2 = branchNameB;
+ } else {
+ renames1 = renamesB;
+ renames2 = renamesA;
+ renames2Dst = &byDstA;
+ branchName1 = branchNameB;
+ branchName2 = branchNameA;
+ struct rename *tmp = ren2;
+ ren2 = ren1;
+ ren1 = tmp;
+ }
+
+ ren1->dst_entry->processed = 1;
+ ren1->src_entry->processed = 1;
+
+ if ( ren1->processed )
+ 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.src != ren2.src");
+ ren2->dst_entry->processed = 1;
+ ren2->processed = 1;
+ 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);
+ conflict_rename_rename(ren1, branchName1, ren2, branchName2);
+ } else {
+ remove_file(1, ren1_src);
+ struct merge_file_info mfi;
+ 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);
+
+ 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(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(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ } else {
+ /* Renamed in 1, maybe changed in 2 */
+ remove_file(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;
+
+ 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);
+ conflict_rename_dir(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,
+ branchName2);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+ } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) {
+ 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);
+ newPath = unique_path(ren1_dst, branchName2);
+ output("Adding as %s instead", newPath);
+ update_file(0, dstShaOtherBranch, dstModeOtherBranch, newPath);
+ } else if ( (ren2 = find_rename_bysrc(renames2Dst, ren1_dst)) ) {
+ cleanMerge = 0;
+ ren2->processed = 1;
+ output("CONFLICT (rename/rename): Rename %s->%s in %s. "
+ "Rename %s->%s in %s",
+ ren1_src, ren1_dst, branchName1,
+ ren2->pair->one->path, ren2->pair->two->path, branchName2);
+ conflict_rename_rename_2(ren1, branchName1, ren2, branchName2);
+ } else
+ tryMerge = 1;
+
+ if ( tryMerge ) {
+ 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->pair->one->mode;
+ unsigned amode = ren1->pair->two->mode;
+ unsigned bmode = srcModeOtherBranch;
+ 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;
+
+ 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(ren1_dst,
+ osha, omode,
+ asha, amode,
+ bsha, bmode,
+ 1 /* clear */);
+ }
+ update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ }
+ }
+ path_list_clear(&srcNames, 0);
+ debug(" processRenames done\n");
+
+ if (cache_dirty)
+ flush_cache();
+ 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(const char *path, struct stage_data *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;
+ 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(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(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(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(0, path);
+ update_file(0, sha, mode, newPath);
+ } else {
+ output("Adding %s", path);
+ update_file(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(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(0, path);
+ update_file(0, aSha, aMode, newPath1);
+ update_file(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(1, mfi.sha, mfi.mode, path);
+ else {
+ cleanMerge = 0;
+ output("CONFLICT (content): Merge conflict in %s", path);
+
+ if ( index_only )
+ update_file(0, mfi.sha, mfi.mode, path);
+ else
+ update_file_flags(mfi.sha, mfi.mode, path,
+ 0 /* updateCache */, 1 /* updateWd */);
+ }
+ } else
+ die("Fatal merge failure, shouldn't happen.");
+
+ if (cache_dirty)
+ flush_cache();
+
+ 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 ) {
+ path_list_clear(¤tFileSet, 1);
+ path_list_clear(¤tDirectorySet, 1);
+ get_files_dirs(head, ¤tFileSet, ¤tDirectorySet);
+ get_files_dirs(merge, ¤tFileSet, ¤tDirectorySet);
+
+ struct path_list *entries = get_unmerged();
+ struct path_list *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");
+ int i;
+ for (i = 0; i < entries->nr; i++) {
+ const char *path = entries->items[i].path;
+ struct stage_data *e = entries->items[i].util;
+ if (e->processed)
+ continue;
+ if (!process_entry(path, e, branch1Name, branch2Name))
+ result.clean = 0;
+ }
+
+ free_rename_entries(&re_merge);
+ free_rename_entries(&re_head);
+ free_index_entries(&entries);
+
+ if (result.clean || index_only)
+ result.tree = git_write_tree();
+ else
+ result.tree = NULL;
+ debug("\t processing entries done\n");
+ } 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;
+}
+
+/*
+ * 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 commit *h1,
+ struct commit *h2,
+ const char *branch1Name,
+ const char *branch2Name,
+ int callDepth /* =0 */,
+ struct commit *ancestor /* =None */)
+{
+ struct merge_result result = { NULL, 0 };
+ const char *msg;
+ int msglen;
+ struct commit_list *ca = NULL, *iter;
+ struct commit *mergedCA;
+ struct merge_tree_result mtr;
+
+ output("Merging:");
+ msg = commit_title(h1, &msglen);
+ /* TODO: refactor. we always show the sha1 with the title */
+ output("%s %.*s", commit_hex_sha1(h1), msglen, msg);
+ msg = commit_title(h2, &msglen);
+ output("%s %.*s", commit_hex_sha1(h2), msglen, msg);
+
+ if ( ancestor )
+ commit_list_insert(ancestor, &ca);
+ else
+ ca = get_merge_bases(h1, h2, 1);
+
+ output("found %u common ancestor(s):", commit_list_count(ca));
+ for (iter = ca; iter; iter = iter->next) {
+ msg = commit_title(iter->item, &msglen);
+ output("%s %.*s", commit_hex_sha1(iter->item), msglen, msg);
+ }
+
+ mergedCA = pop_commit(&ca);
+
+ /* TODO: what happens when merge with virtual commits fails? */
+ for (iter = ca; iter; iter = iter->next) {
+ output_indent = callDepth + 1;
+ result = merge(mergedCA, iter->item,
+ "Temporary merge branch 1",
+ "Temporary merge branch 2",
+ 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;
+ }
+
+ mtr = merge_trees(h1->tree, h2->tree,
+ mergedCA->tree, branch1Name, branch2Name);
+
+ if ( !ancestor && (mtr.clean || index_only) ) {
+ result.commit = make_virtual_commit(mtr.tree, "merged tree");
+ commit_list_insert(h1, &result.commit->parents);
+ commit_list_insert(h2, &result.commit->parents->next);
+ } else
+ result.commit = NULL;
+
+ result.clean = mtr.clean;
+ return result;
+}
+
+static struct commit *get_ref(const char *ref)
+{
+ unsigned char sha1[20];
+ struct object *object;
+
+ if (get_sha1(ref, sha1))
+ die("Could not resolve ref '%s'", ref);
+ object = deref_tag(parse_object(sha1), ref, strlen(ref));
+ if (object->type != TYPE_COMMIT)
+ return NULL;
+ if (parse_commit((struct commit *)object))
+ die("Could not parse commit '%s'", sha1_to_hex(object->sha1));
+ return (struct commit *)object;
+}
+
+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.");
+
+ const char *branch1, *branch2;
+
+ branch1 = argv[++i];
+ branch2 = argv[++i];
+ printf("Merging %s with %s\n", branch1, branch2);
+
+ struct merge_result result;
+ struct commit *h1 = get_ref(branch1);
+ struct commit *h2 = get_ref(branch2);
+
+ if (bases_count == 1) {
+ struct commit *ancestor = get_ref(bases[0]);
+ result = merge(h1, h2, branch1, branch2, 0, ancestor);
+ } else
+ result = merge(h1, h2, branch1, branch2, 0, NULL);
+
+ if (cache_dirty)
+ flush_cache();
+
+ 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..f15a10d
--- /dev/null
+++ b/path-list.c
@@ -0,0 +1,105 @@
+#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 *list, const char *path,
+ int *exact_match)
+{
+ int left = -1, right = list->nr;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ int compare = strcmp(path, list->items[middle].path);
+ 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 *list, const char *path)
+{
+ int exact_match;
+ int index = get_entry_index(list, path, &exact_match);
+
+ if (exact_match)
+ return -1 - index;
+
+ if (list->nr + 1 >= list->alloc) {
+ list->alloc += 32;
+ list->items = xrealloc(list->items, list->alloc
+ * sizeof(struct path_list_item));
+ }
+ if (index < list->nr)
+ memmove(list->items + index + 1, list->items + index,
+ (list->nr - index)
+ * sizeof(struct path_list_item));
+ list->items[index].path = list->strdup_paths ?
+ strdup(path) : (char *)path;
+ list->items[index].util = NULL;
+ list->nr++;
+
+ return index;
+}
+
+struct path_list_item *path_list_insert(const char *path, struct path_list *list)
+{
+ int index = add_entry(list, path);
+
+ if (index < 0)
+ index = 1 - index;
+
+ return list->items + 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;
+}
+
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list)
+{
+ int exact_match, i = get_entry_index(list, path, &exact_match);
+ if (!exact_match)
+ return NULL;
+ return list->items + i;
+}
+
+void path_list_clear(struct path_list *list, int free_items)
+{
+ if (list->items) {
+ int i;
+ if (free_items)
+ for (i = 0; i < list->nr; i++) {
+ if (list->strdup_paths)
+ free(list->items[i].path);
+ if (list->items[i].util)
+ free(list->items[i].util);
+ }
+ free(list->items);
+ }
+ list->items = NULL;
+ list->nr = list->alloc = 0;
+}
+
+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:%p\n", p->items[i].path, p->items[i].util);
+}
+
diff --git a/path-list.h b/path-list.h
new file mode 100644
index 0000000..d6401ea
--- /dev/null
+++ b/path-list.h
@@ -0,0 +1,22 @@
+#ifndef _PATH_LIST_H_
+#define _PATH_LIST_H_
+
+struct path_list_item {
+ char *path;
+ void *util;
+};
+struct path_list
+{
+ struct path_list_item *items;
+ unsigned int nr, alloc;
+ unsigned int strdup_paths:1;
+};
+
+void print_path_list(const char *text, const struct path_list *p);
+
+int path_list_has_path(const struct path_list *list, const char *path);
+void path_list_clear(struct path_list *list, int free_items);
+struct path_list_item *path_list_insert(const char *path, struct path_list *list);
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
+
+#endif /* _PATH_LIST_H_ */
diff --git a/read-cache.c b/read-cache.c
index 3c32aae..f6e1b70 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -24,6 +24,11 @@ unsigned int active_nr = 0, active_alloc
struct cache_tree *active_cache_tree = NULL;
+int cache_errno = 0;
+
+static void *cache_mmap = NULL;
+static size_t cache_mmap_size = 0;
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -577,22 +582,6 @@ int add_cache_entry(struct cache_entry *
return 0;
}
-/* Three functions to allow overloaded pointer return; see linux/err.h */
-static inline void *ERR_PTR(long error)
-{
- return (void *) error;
-}
-
-static inline long PTR_ERR(const void *ptr)
-{
- return (long) ptr;
-}
-
-static inline long IS_ERR(const void *ptr)
-{
- return (unsigned long)ptr > (unsigned long)-1000L;
-}
-
/*
* "refresh" does not calculate a new sha1 file or bring the
* cache up-to-date for mode/content changes. But what it
@@ -604,14 +593,16 @@ static inline long IS_ERR(const void *pt
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
-static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
{
struct stat st;
struct cache_entry *updated;
int changed, size;
- if (lstat(ce->name, &st) < 0)
- return ERR_PTR(-errno);
+ if (lstat(ce->name, &st) < 0) {
+ cache_errno = errno;
+ return NULL;
+ }
changed = ce_match_stat(ce, &st, really);
if (!changed) {
@@ -619,11 +610,13 @@ static struct cache_entry *refresh_entry
!(ce->ce_flags & htons(CE_VALID)))
; /* mark this one VALID again */
else
- return NULL;
+ return ce;
}
- if (ce_modified(ce, &st, really))
- return ERR_PTR(-EINVAL);
+ if (ce_modified(ce, &st, really)) {
+ cache_errno = EINVAL;
+ return NULL;
+ }
size = ce_size(ce);
updated = xmalloc(size);
@@ -666,13 +659,13 @@ int refresh_cache(unsigned int flags)
continue;
}
- new = refresh_entry(ce, really);
- if (!new)
+ new = refresh_cache_entry(ce, really);
+ if (new == ce)
continue;
- if (IS_ERR(new)) {
- if (not_new && PTR_ERR(new) == -ENOENT)
+ if (!new) {
+ if (not_new && cache_errno == ENOENT)
continue;
- if (really && PTR_ERR(new) == -EINVAL) {
+ if (really && cache_errno == EINVAL) {
/* If we are doing --really-refresh that
* means the index is not valid anymore.
*/
@@ -729,39 +722,43 @@ static int read_index_extension(const ch
int read_cache(void)
{
+ return read_cache_from(get_index_file());
+}
+
+/* remember to discard_cache() before reading a different cache! */
+int read_cache_from(const char *path)
+{
int fd, i;
struct stat st;
- unsigned long size, offset;
- void *map;
+ unsigned long offset;
struct cache_header *hdr;
errno = EBUSY;
- if (active_cache)
+ if (cache_mmap)
return active_nr;
errno = ENOENT;
index_file_timestamp = 0;
- fd = open(get_index_file(), O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
return 0;
die("index file open failed (%s)", strerror(errno));
}
- size = 0; // avoid gcc warning
- map = MAP_FAILED;
+ cache_mmap = MAP_FAILED;
if (!fstat(fd, &st)) {
- size = st.st_size;
+ cache_mmap_size = st.st_size;
errno = EINVAL;
- if (size >= sizeof(struct cache_header) + 20)
- map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (cache_mmap_size >= sizeof(struct cache_header) + 20)
+ cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
}
close(fd);
- if (map == MAP_FAILED)
+ if (cache_mmap == MAP_FAILED)
die("index file mmap failed (%s)", strerror(errno));
- hdr = map;
- if (verify_hdr(hdr, size) < 0)
+ hdr = cache_mmap;
+ if (verify_hdr(hdr, cache_mmap_size) < 0)
goto unmap;
active_nr = ntohl(hdr->hdr_entries);
@@ -770,12 +767,12 @@ int read_cache(void)
offset = sizeof(*hdr);
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);
+ struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset);
offset = offset + ce_size(ce);
active_cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
- while (offset <= size - 20 - 8) {
+ while (offset <= cache_mmap_size - 20 - 8) {
/* After an array of active_nr index entries,
* there can be arbitrary number of extended
* sections, each of which is prefixed with
@@ -783,10 +780,10 @@ int read_cache(void)
* in 4-byte network byte order.
*/
unsigned long extsize;
- memcpy(&extsize, (char *) map + offset + 4, 4);
+ memcpy(&extsize, (char *) cache_mmap + offset + 4, 4);
extsize = ntohl(extsize);
- if (read_index_extension(((const char *) map) + offset,
- (char *) map + offset + 8,
+ if (read_index_extension(((const char *) cache_mmap) + offset,
+ (char *) cache_mmap + offset + 8,
extsize) < 0)
goto unmap;
offset += 8;
@@ -795,11 +792,28 @@ int read_cache(void)
return active_nr;
unmap:
- munmap(map, size);
+ munmap(cache_mmap, cache_mmap_size);
errno = EINVAL;
die("index file corrupt");
}
+int discard_cache()
+{
+ int ret;
+
+ if (cache_mmap == NULL)
+ return 0;
+ ret = munmap(cache_mmap, cache_mmap_size);
+ cache_mmap = NULL;
+ cache_mmap_size = 0;
+ active_nr = active_cache_changed = 0;
+ index_file_timestamp = 0;
+ cache_tree_free(&active_cache_tree);
+
+ /* no need to throw away allocated active_cache */
+ return ret;
+}
+
#define WRITE_BUFFER_SIZE 8192
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
static unsigned long write_buffer_len;
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index d34c6cf..b70e177 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -51,7 +51,7 @@ test_expect_success setup '
'
test_expect_success 'reference merge' '
- git merge -s recursive "reference merge" HEAD master
+ git merge -s recur "reference merge" HEAD master
'
test_expect_success rebase '
^ permalink raw reply related
* [RFC] just an (stupid) idea when creating a new branch
From: moreau francis @ 2006-07-08 15:55 UTC (permalink / raw)
To: git
Hi GIT folks.
I'm a complete newbie on git development so excuse me if
this idea is completely stupid.
Would it be possible to let the user stick a short explanation
on what a branch is supposed to implement during its creation.
That is
$ git branch --topic "Implement a killer feature \
> This set of patches add the foo feature to GIT" mybranch master
Then it would be possible with another command to consult this topic
after a while. And even more useful, when generating the patch set
with the following git command
$ git-format-patch -n HEAD master
it uses the topic branch to generate as first patch a summary
of the patch serie. That is
"""
Subject: [PATCH 0/n] Implement a killer feature
This This set of patches add the foo feature to GIT.
"""
Is it useless ?
Thanks
Francis
^ permalink raw reply
* [Patch] Using 'perl' in *.sh
From: Michal Rokos @ 2006-07-08 15:32 UTC (permalink / raw)
To: git
Hi,
some GIT's shell script are using bare 'perl' for perl invocation. It's
causing me problems... I compile git with PERL_PATH set and I'd suggest to
use it everywhere.
So @@PERL@@ would be replaced with PERL_PATH_SQ instead.
What do you think?
Michal
Signed-off-by: Michal Rokos <michal.rokos@nextsoft.cz>
diff --git a/Makefile b/Makefile
index 202f261..8f9881f 100644
--- a/Makefile
+++ b/Makefile
@@ -514,6 +514,7 @@ common-cmds.h: Documentation/git-*.txt
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's!@@PERL@@!$(PERL_PATH_SQ)!g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
diff --git a/git-bisect.sh b/git-bisect.sh
index 03df143..06a8d26 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -13,7 +13,7 @@ git bisect log show bisect log.'
. git-sh-setup
sq() {
- perl -e '
+ @@PERL@@ -e '
for (@ARGV) {
s/'\''/'\'\\\\\'\''/g;
print " '\''$_'\''";
diff --git a/git-clone.sh b/git-clone.sh
index 6a14b25..0368803 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -324,7 +324,7 @@ test -d "$GIT_DIR/refs/reference-tmp" &&
if test -f "$GIT_DIR/CLONE_HEAD"
then
# Read git-fetch-pack -k output and store the remote branches.
- perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
+ @@PERL@@ -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
fi
cd "$D" || exit
diff --git a/git-commit.sh b/git-commit.sh
index 22c4ce8..08d786d 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -147,7 +147,7 @@ #'
git-ls-files -z --others $option \
--exclude-per-directory=.gitignore
fi |
- perl -e '$/ = "\0";
+ @@PERL@@ -e '$/ = "\0";
my $shown = 0;
while (<>) {
chomp;
diff --git a/git-fetch.sh b/git-fetch.sh
index 48818f8..f80299d 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -278,7 +278,7 @@ fetch_main () {
head="ref: $remote_name"
while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
do
- remote_name_quoted=$(perl -e '
+ remote_name_quoted=$(@@PERL@@ -e '
my $u = $ARGV[0];
$u =~ s/^ref:\s*//;
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
diff --git a/git-rebase.sh b/git-rebase.sh
index 3945e06..1b9e986 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -311,7 +311,7 @@ echo "$prev_head" > "$dotest/prev_head"
msgnum=0
for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
- | perl -e 'print reverse <>'`
+ | @@PERL@@ -e 'print reverse <>'`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"
--
Michal Rokos
NextSoft s.r.o.
Vyskočilova 1/1410
140 21 Praha 4
phone: +420 267 224 311
fax: +420 267 224 307
mobile: +420 736 646 591
e-mail: michal.rokos@nextsoft.cz
^ permalink raw reply related
* [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Michal Rokos @ 2006-07-08 15:27 UTC (permalink / raw)
To: git
Johannes,
in commit 07002287f3e219a16a948a8a6eca0a41162a491f
you cleaned up 'replace ugly and unportable sed invocation' as you said.
Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
and 'rfile'.
Michal
Signed-off-by: Michal Rokos <michal.rokos@nextsoft.cz>
diff --git a/Makefile b/Makefile
index 202f261..8f9881f 100644
--- a/Makefile
+++ b/Makefile
@@ -552,9 +553,9 @@ git-instaweb: git-instaweb.sh gitweb/git
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
- -e '/@@GITWEB_CGI@@/rgitweb/gitweb.cgi' \
+ -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-e '/@@GITWEB_CGI@@/d' \
- -e '/@@GITWEB_CSS@@/rgitweb/gitweb.css' \
+ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
-e '/@@GITWEB_CSS@@/d' \
$@.sh > $@+
chmod +x $@+
--
Michal Rokos
NextSoft s.r.o.
Vyskočilova 1/1410
140 21 Praha 4
phone: +420 267 224 311
fax: +420 267 224 307
mobile: +420 736 646 591
e-mail: michal.rokos@nextsoft.cz
^ permalink raw reply related
* Re: [PATCH] Close the index file between writing and committing
From: Junio C Hamano @ 2006-07-08 10:28 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0607081055520.29667@wbgn013.biozentrum.uni-wuerzburg.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Alternatively, write_cache() could be taught to close the file
> itself, but maybe there will be a user of write_cache() who wants
> to write something after the cache data?
Currently I suspect nobody would want to append stuff to the
index file, because anybody who wants to add something to the
cache can do so with the index extension mechanism.
However, it is conceivable that later we might invent a new
program that writes the index file and other things to a single
stream, and closing the fd in write_cache() would inconvenience
such a program.
^ permalink raw reply
* [PATCH] Close the index file between writing and committing
From: Johannes Schindelin @ 2006-07-08 8:56 UTC (permalink / raw)
To: git, junkio
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
This is just playing it safe.
Alternatively, write_cache() could be taught to close the file
itself, but maybe there will be a user of write_cache() who wants
to write something after the cache data?
builtin-add.c | 2 +-
builtin-apply.c | 2 +-
builtin-read-tree.c | 2 +-
builtin-rm.c | 2 +-
builtin-update-index.c | 2 +-
builtin-write-tree.c | 3 ++-
checkout-index.c | 2 +-
7 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/builtin-add.c b/builtin-add.c
index bfbbb1b..2d25698 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -181,7 +181,7 @@ int cmd_add(int argc, const char **argv,
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-apply.c b/builtin-apply.c
index e9ead00..c3af489 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2323,7 +2323,7 @@ int cmd_apply(int argc, const char **arg
if (write_index) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 9a2099d..23a8d92 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -1038,7 +1038,7 @@ int cmd_read_tree(int argc, const char *
}
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("unable to write new index file");
return 0;
}
diff --git a/builtin-rm.c b/builtin-rm.c
index 4d56a1f..875d825 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -147,7 +147,7 @@ int cmd_rm(int argc, const char **argv,
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-update-index.c b/builtin-update-index.c
index ef50243..1a4200d 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -648,7 +648,7 @@ int cmd_update_index(int argc, const cha
finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(lock_file))
+ close(newfd) || commit_lock_file(lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 70e9b6f..449a4d1 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -35,7 +35,8 @@ int write_tree(unsigned char *sha1, int
missing_ok, 0) < 0)
die("git-write-tree: error building trees");
if (0 <= newfd) {
- if (!write_cache(newfd, active_cache, active_nr))
+ if (!write_cache(newfd, active_cache, active_nr)
+ && !close(newfd))
commit_lock_file(lock_file);
}
/* Not being able to write is fine -- we are only interested
diff --git a/checkout-index.c b/checkout-index.c
index ea40bc2..2927955 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -311,7 +311,7 @@ int main(int argc, char **argv)
if (0 <= newfd &&
(write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file)))
+ close(newfd) || commit_lock_file(&lock_file)))
die("Unable to write new index file");
return 0;
}
--
1.4.1.rc2.g5b68a
^ permalink raw reply related
* [PATCH] templates/hooks--update: replace diffstat calls with git diff --stat
From: Eric Wong @ 2006-07-08 8:50 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20060708084121.GD29036@hand.yhbt.net>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
This was part of the previous patch, but useful on its own since
we have our own internal diffstat
templates/hooks--update | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/templates/hooks--update b/templates/hooks--update
index d7a8f0a..76d5ac2 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -60,7 +60,7 @@ then
echo "Changes since $prev:"
git rev-list --pretty $prev..$3 | $short
echo ---
- git diff $prev..$3 | diffstat -p1
+ git diff --stat $prev..$3
echo ---
fi
;;
@@ -75,7 +75,7 @@ else
base=$(git-merge-base "$2" "$3")
case "$base" in
"$2")
- git diff "$3" "^$base" | diffstat -p1
+ git diff --stat "$3" "^$base"
echo
echo "New commits:"
;;
--
1.4.1.g6a62
^ permalink raw reply related
* Re: [PATCH] Fix several places where diff.renames in config can be problematic
From: Eric Wong @ 2006-07-08 8:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vsllcr077.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
>
> > -my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
> > +my @files = safe_pipe_capture('git-diff-tree','--no-renames','-r',
> > + $parent, $commit);
>
> I changed my mind.
>
> -- >8 --
> diff: do not use configuration magic at the core-level
>
> The Porcelainish has become so much usable as the UI that there
> is not much reason people should be using the core programs by
> hand anymore. At this point we are better off making the
> behaviour of the core programs predictable by keeping them
> unaffected by the configuration variables. Otherwise they will
> become very hard to use as reliable building blocks.
>
> For example, "git-commit -a" internally uses git-diff-files to
> figure out the set of paths that need to be updated in the
> index, and we should never allow diff.renames that happens to be
> in the configuration to interfere (or slow down the process).
>
> The UI level configuration such as showing renamed diff and
> coloring are still honored by the Porcelainish ("git log" family
> and "git diff"), but not by the core anymore.
Full ack on this. I was ready to let my diff.renames patch drop
if there were too many potential incompatibilities/breakages,
but this should alleviate that.
I should work on breaking out of the habit of using git
diff-{index,tree} in my day-to-day use and finally start using git diff
more to save some keystrokes.
--
Eric Wong
^ permalink raw reply
* Re: [PATCH] Fix several places where diff.renames in config can be problematic
From: Junio C Hamano @ 2006-07-08 8:05 UTC (permalink / raw)
To: Eric Wong; +Cc: git
In-Reply-To: <20060708015844.GA13769@soma>
Eric Wong <normalperson@yhbt.net> writes:
> -my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
> +my @files = safe_pipe_capture('git-diff-tree','--no-renames','-r',
> + $parent, $commit);
I changed my mind.
-- >8 --
diff: do not use configuration magic at the core-level
The Porcelainish has become so much usable as the UI that there
is not much reason people should be using the core programs by
hand anymore. At this point we are better off making the
behaviour of the core programs predictable by keeping them
unaffected by the configuration variables. Otherwise they will
become very hard to use as reliable building blocks.
For example, "git-commit -a" internally uses git-diff-files to
figure out the set of paths that need to be updated in the
index, and we should never allow diff.renames that happens to be
in the configuration to interfere (or slow down the process).
The UI level configuration such as showing renamed diff and
coloring are still honored by the Porcelainish ("git log" family
and "git diff"), but not by the core anymore.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
builtin-diff-files.c | 2 +-
builtin-diff-index.c | 2 +-
builtin-diff-stages.c | 2 +-
builtin-diff-tree.c | 2 +-
builtin-diff.c | 2 +-
builtin-log.c | 8 ++++----
diff.c | 8 +++++++-
diff.h | 2 +-
8 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index a655eea..81ac2fe 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -18,7 +18,7 @@ int cmd_diff_files(int argc, const char
struct rev_info rev;
int silent = 0;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
rev.abbrev = 0;
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index b37c9e8..a1fa1b8 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -15,7 +15,7 @@ int cmd_diff_index(int argc, const char
int cached = 0;
int i;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
rev.abbrev = 0;
diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c
index 30931fe..9c62702 100644
--- a/builtin-diff-stages.c
+++ b/builtin-diff-stages.c
@@ -61,7 +61,7 @@ int cmd_diff_stages(int ac, const char *
const char *prefix = setup_git_directory();
const char **pathspec = NULL;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
read_cache();
diff_setup(&diff_options);
while (1 < ac && av[1][0] == '-') {
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index ae1cde9..b610668 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -67,7 +67,7 @@ int cmd_diff_tree(int argc, const char *
static struct rev_info *opt = &log_tree_opt;
int read_stdin = 0;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
nr_sha1 = 0;
init_revisions(opt);
opt->abbrev = 0;
diff --git a/builtin-diff.c b/builtin-diff.c
index d520c7c..1df531b 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -250,7 +250,7 @@ int cmd_diff(int argc, const char **argv
* Other cases are errors.
*/
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
argc = setup_revisions(argc, argv, &rev, NULL);
diff --git a/builtin-log.c b/builtin-log.c
index 698b71e..dd5a5a2 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -47,7 +47,7 @@ int cmd_whatchanged(int argc, const char
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.diff = 1;
rev.diffopt.recursive = 1;
@@ -62,7 +62,7 @@ int cmd_show(int argc, const char **argv
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.diff = 1;
rev.diffopt.recursive = 1;
@@ -79,7 +79,7 @@ int cmd_log(int argc, const char **argv,
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.always_show_header = 1;
cmd_log_init(argc, argv, envp, &rev);
@@ -105,7 +105,7 @@ static int git_format_config(const char
strcat(extra_headers, value);
return 0;
}
- return git_diff_config(var, value);
+ return git_diff_ui_config(var, value);
}
diff --git a/diff.c b/diff.c
index 1bf1ed0..493650c 100644
--- a/diff.c
+++ b/diff.c
@@ -102,7 +102,13 @@ static const char *parse_diff_color_valu
die("bad config value '%s' for variable '%s'", value, var);
}
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
{
if (!strcmp(var, "diff.renamelimit")) {
diff_rename_limit_default = git_config_int(var, value);
diff --git a/diff.h b/diff.h
index 8ab0448..a06f959 100644
--- a/diff.h
+++ b/diff.h
@@ -123,7 +123,7 @@ #define DIFF_SETUP_REVERSE 1
#define DIFF_SETUP_USE_CACHE 2
#define DIFF_SETUP_USE_SIZE_CACHE 4
-extern int git_diff_config(const char *var, const char *value);
+extern int git_diff_ui_config(const char *var, const char *value);
extern void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int);
extern int diff_setup_done(struct diff_options *);
^ permalink raw reply related
* Re: git on HP-UX
From: Pavel Roskin @ 2006-07-08 7:38 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vfyhe465i.fsf@assigned-by-dhcp.cox.net>
On Thu, 2006-07-06 at 17:20 -0700, Junio C Hamano wrote:
> Pavel Roskin <proski@gnu.org> writes:
> > I hope the Autoconf based configure is on its way to git, but I don't
> > see in in the "pu" branch yet. I'm not very keen about reinventing
> > Autoconf and hacking a hand-made configure script.
>
> OK, you half-convinced me. The other half came from a recent
> series of patches to try using 'which' to detect executables,
> which is another common mistake handcrafted configure script
> makes, which autoconf people have solved for us long time ago.
I really appreciate it. I hope all the Autoconf patches in the queue
will land in "pu" shortly, and then I'm going to have a closer look at
it.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: Quick merge status updates.
From: Pavel Roskin @ 2006-07-08 7:36 UTC (permalink / raw)
To: Petr Baudis; +Cc: Junio C Hamano, git
In-Reply-To: <20060702204906.GG29115@pasky.or.cz>
On Sun, 2006-07-02 at 22:49 +0200, Petr Baudis wrote:
> Dear diary, on Wed, Jun 28, 2006 at 12:05:03PM CEST, I got a letter
> where Pavel Roskin <proski@gnu.org> said that...
> I feel that it is time for another stupid question of mine - why can't
> you just use lib?
>
> use lib ('@@INSTLIBDIR@@');
>
> Looks a lot better than some @INC unshifting, and it should be
> equivalent.
I honestly have no idea. I think whatever gets you closer to the
satisfaction of the "three conditions" should be used.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: [RFC/PATCH 14] autoconf: Added --with/--without for openssl, curl, expat to ./configure
From: Pavel Roskin @ 2006-07-08 7:33 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <200607011955.23908.jnareb@gmail.com>
Hello, Jakub!
On Sat, 2006-07-01 at 19:55 +0200, Jakub Narebski wrote:
> >> I suspect that AS_HELP_WITH does some strange quoting, or stripping. Both
> >> [=PATH] and [[=PATH]] produces =PATH in ./configure --help output.
> >> When using @<:@=PATH@:>@ I get [=PATH], but the description of option begins
> >> line below.
Sorry, I misunderstood the problem. I think it's pure cosmetics.
Please don't let it stop you.
> I guess I would just not use AS_HELP_STRING, and format help
> message "by hand".
Please don't wast time on such minor things. It's more important to get
the functionality implemented.
> By the way, if you know autoconf well, perhaps you could tell me how to write
> tests for the following programs: ar, tar, rpmbuild, how to write test for
> Python version (or rather for WITH_OWN_SUBPROCESS_PY) and other test autoconf.ac
> lacks now (NEEDS_SSL_WITH_CRYPTO, NEEDS_LIBICONV, NEEDS_SOCKET, NO_MMAP,
> NO_IPV6, NO_ICONV, NO_ACCURATE_DIFF unless that was removed or changed name).
Generally, see the Autoconf manual for the specific test first, then for
more common test.
For ar, use AC_CHECK_TOOL to allow cross-compilation. For tar and
rpmbiuld, use
AC_CHECK_PROG. Python will have to run to find the version, I'm afraid,
which would complicate cross builds. Fortunately, it's on the way out.
Tests for sockets are described in the Autoconf documentation. Other
tests should probably be implemented as test programs unless they can be
reduced to checking for a specific symbol in a specific library.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: [PATCH] gitweb.cgi: Use File::MMagic; "a=blob" action knows the blob/file type
From: Junio C Hamano @ 2006-07-08 6:18 UTC (permalink / raw)
To: ltuikov; +Cc: git
In-Reply-To: <20060708041021.24704.qmail@web31804.mail.mud.yahoo.com>
Luben Tuikov <ltuikov@yahoo.com> writes:
> Use File::MMagic to determine the MIME type of a blob/file.
> The variable magic_mime_file holds the location of the
> "magic.mime" file, usually "/usr/share/file/magic.mime".
> If not defined, the magic numbers internally stored in the
> File::MMagic module are used.
I am sorry to ask you this, but would you mind redoing this
patch without File::MMagic bits? I think giving "a=blob" an
ability to automatically switch to git_blob_plain is a good
addition (as is your earlier patch to give a direct link to
reach blob_plain from the list), so let's have that part in
first. I haven't applied your earlier one but it will appear in
"next" shortly.
Existing filename based mimetypes_guess should be a lot cheaper
than exploding a blob and feeding it to File::MMagic. I was
hoping File::MMagic to be used when we cannot guess the content
type that way (i.e. when mimetypes_guess returns undef or
application/octet-stream).
Since the repository owner can correct misidentification by the
standard /etc/mime.types by supplying a custom per-repository
$mimetypes_file (modulo that the current implementation of
mimetype_guess_file does not allow it if the file does not have
an extension that is specific enough), File::MMagic might be an
overkill, especially if used in the way this patch does. To
allow finer grained differentiation that cannot be done with
file extensions alone (e.g. some files may have .dat extension
but one can be VCD mpeg wrapped in RIFF, and another can be a
Z-machine story file), it might be simpler to allow the
repository owner to specify full $file_name for such an ambiguous
file in their custom $mimetypes_file, and try to match it in
mimetype_guess_file sub. That way we may not even need to use
File::MMagic.
Are there cases where only $hash is given without $file_name?
If so we may need to fall back on File::MMagic in such a case
after all, but get_blob_mimetype sub copies the whole blob to a
temporary file to work around a problem with version 1.27 you
state in the comment -- this is way too much (and nobody seems
to clean up the tempfile). Looking at magic.mime, I suspect we
might be able to get away with the first 4k bytes or so at most
(the largest offset except iso9660 image is "Biff5" appearing at
2114 to signal an Excel spreadsheet).
^ permalink raw reply
* [PATCH] gitweb.cgi: Use File::MMagic; "a=blob" action knows the blob/file type
From: Luben Tuikov @ 2006-07-08 4:10 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 775 bytes --]
Use File::MMagic to determine the MIME type of a blob/file.
The variable magic_mime_file holds the location of the
"magic.mime" file, usually "/usr/share/file/magic.mime".
If not defined, the magic numbers internally stored in the
File::MMagic module are used.
Action "blob" knows the file type: if the file type is
not "text/*" then action "blob" defaults to "blob_plain",
i.e. the file is downloaded raw for the browser to interpret.
If the file type is "text/*", then "blob" defaults to the
current "cat -n"-like output, from which you can click
"plain", to get the "blob_plain" output.
Signed-off-by: Luben Tuikov <ltuikov@yahoo.com>
---
gitweb/gitweb.cgi | 140 +++++++++++++++++++++--------------------------------
1 files changed, 56 insertions(+), 84 deletions(-)
[-- Attachment #2: pat785262450 --]
[-- Type: text/plain, Size: 5173 bytes --]
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index cce0753..6798990 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -14,6 +14,8 @@ use CGI::Util qw(unescape);
use CGI::Carp qw(fatalsToBrowser);
use Encode;
use Fcntl ':mode';
+use File::MMagic;
+use FileHandle;
binmode STDOUT, ':utf8';
our $cgi = new CGI;
@@ -54,9 +56,15 @@ #our $projects_list = $projectroot;
our $projects_list = "index/index.aux";
# default blob_plain mimetype and default charset for text/plain blob
-our $default_blob_plain_mimetype = 'text/plain';
+our $default_blob_mimetype = 'text/plain';
our $default_text_plain_charset = undef;
+# magic_mime_file: if defined this file will be used by File::MMagic
+# to guess the file type, else the magic numbers stored internally
+# in File::MMagic will be used. Either relative or absolute name
+# can be given. E.g. "/usr/share/file/magic.mime".
+our $magic_mime_file = undef;
+
# file to use for guessing MIME types before trying /etc/mime.types
# (relative to the current git repository)
our $mimetypes_file = undef;
@@ -1455,11 +1463,58 @@ sub git_get_hash_by_path {
}
}
+#
+# Strangely enough the File::MMagic package, version 1.27, has a bug
+# whereby reading from a piped filehandle (e.g. STDIN, or "-|") always
+# returns 'text/plain', but reading from a file on a file system (as it
+# would be the case for the checktype_filename() method) properly
+# determines the file type.
+#
+sub get_blob_mimetype {
+ my $blob_file = "$git_temp/blob-$hash";
+ if (! -r $blob_file) {
+ open my $fd_in, "-|", "$gitbin/git-cat-file blob $hash" or return $default_blob_mimetype;
+ open my $fd_out, "> $blob_file";
+ my @file = <$fd_in>;
+ print $fd_out @file;
+ close $fd_out;
+ close $fd_in;
+ }
+ my $mm = $magic_mime_file ? File::MMagic->new($magic_mime_file) : new File::MMagic;
+ my $mime = $mm->checktype_filename($blob_file);
+ return $mime;
+}
+
+sub git_blob_plain {
+ open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+ my $mimetype = get_blob_mimetype();
+
+ # save as filename, even when no $file_name is given
+ my $save_as = "$hash";
+ if (defined $file_name) {
+ $save_as = $file_name;
+ } elsif ($mimetype =~ m/^text\//) {
+ $save_as .= '.txt';
+ }
+
+ print $cgi->header(-type => "$mimetype", '-content-disposition' => "inline; filename=\"$save_as\"");
+ undef $/;
+ binmode STDOUT, ':raw';
+ print <$fd>;
+ binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+ $/ = "\n";
+ close $fd;
+}
+
sub git_blob {
if (!defined $hash && defined $file_name) {
my $base = $hash_base || git_read_head($project);
$hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
}
+ my $mimetype = get_blob_mimetype();
+ if ($mimetype !~ m/^text\//) {
+ return git_blob_plain();
+ }
my $have_blame = git_get_project_config_bool ('blame');
open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
git_header_html();
@@ -1510,89 +1565,6 @@ sub git_blob {
git_footer_html();
}
-sub mimetype_guess_file {
- my $filename = shift;
- my $mimemap = shift;
- -r $mimemap or return undef;
-
- my %mimemap;
- open(MIME, $mimemap) or return undef;
- while (<MIME>) {
- my ($mime, $exts) = split(/\t+/);
- my @exts = split(/\s+/, $exts);
- foreach my $ext (@exts) {
- $mimemap{$ext} = $mime;
- }
- }
- close(MIME);
-
- $filename =~ /\.(.*?)$/;
- return $mimemap{$1};
-}
-
-sub mimetype_guess {
- my $filename = shift;
- my $mime;
- $filename =~ /\./ or return undef;
-
- if ($mimetypes_file) {
- my $file = $mimetypes_file;
- #$file =~ m#^/# or $file = "$projectroot/$path/$file";
- $mime = mimetype_guess_file($filename, $file);
- }
- $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
- return $mime;
-}
-
-sub git_blob_plain_mimetype {
- my $fd = shift;
- my $filename = shift;
-
- # just in case
- return $default_blob_plain_mimetype unless $fd;
-
- if ($filename) {
- my $mime = mimetype_guess($filename);
- $mime and return $mime;
- }
-
- if (-T $fd) {
- return 'text/plain' .
- ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
- } elsif (! $filename) {
- return 'application/octet-stream';
- } elsif ($filename =~ m/\.png$/i) {
- return 'image/png';
- } elsif ($filename =~ m/\.gif$/i) {
- return 'image/gif';
- } elsif ($filename =~ m/\.jpe?g$/i) {
- return 'image/jpeg';
- } else {
- return 'application/octet-stream';
- }
-}
-
-sub git_blob_plain {
- open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
- my $type = git_blob_plain_mimetype($fd, $file_name);
-
- # save as filename, even when no $file_name is given
- my $save_as = "$hash";
- if (defined $file_name) {
- $save_as = $file_name;
- } elsif ($type =~ m/^text\//) {
- $save_as .= '.txt';
- }
-
- print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
- undef $/;
- binmode STDOUT, ':raw';
- print <$fd>;
- binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
- $/ = "\n";
- close $fd;
-}
-
sub git_tree {
if (!defined $hash) {
$hash = git_read_head($project);
--
1.4.1.g2f3c
^ permalink raw reply related
* Re: comparing file contents in is_exact_match?
From: Johannes Schindelin @ 2006-07-08 2:50 UTC (permalink / raw)
To: Florian Weimer; +Cc: git
In-Reply-To: <87k66p8jee.fsf@mid.deneb.enyo.de>
Hi,
On Fri, 7 Jul 2006, Florian Weimer wrote:
> - s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
> + s->data = mmap(NULL, s->size, PROT_READ, MAP_SHARED, fd, 0);
Be advised that this breaks setups with NO_MMAP, in particular most (all)
cygwin setups I know of.
Hth,
Dscho
^ permalink raw reply
* Re: Does Git run on Windows ?
From: Johannes Schindelin @ 2006-07-08 2:48 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <e8mb8r$t1u$2@sea.gmane.org>
Hi,
On Fri, 7 Jul 2006, Jakub Narebski wrote:
> Johannes Schindelin wrote:
>
> > On Thu, 6 Jul 2006, Aaron Gray wrote:
> >
> >> Its got lots of C code, and Bash scripts, with a couple of Perl scripts.
> >
> > And you completely forgot Python.
>
> If I remember correctly the only Python dependency was recursive merge
> strategy, currently being reworked in C.
I remember, too. Because Alex and me are working very hard to provide
something which is presentable to the list. We will release a WIP patch of
it, probably tomorrow.
Ciao,
Dscho
^ permalink raw reply
* Re: What's in git.git
From: Johannes Schindelin @ 2006-07-08 2:28 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vlkr5szi2.fsf@assigned-by-dhcp.cox.net>
Hi,
On Fri, 7 Jul 2006, Junio C Hamano wrote:
> - Early parts of Perly Git by Petr Baudis with help from others
> are merged to "next". Please report breakages before other
> Perl scripts are converted to use this.
I saw many problems on my machines with this, just as I expected. Please
play it _really_ safe. I hated to be forced to rewrite git-fmt-merge-msg
in C, but I just could not use 'next' without that. I would hate it even
more to rewrite git-mv in C just to make the tests happy (they are not on
my iBook right now).
> - GIT_TRACE by Matthias Lederhofer. What it lets you do is
> interesting (although I personally do not foresee myself
> using it), and I am in favor of its inclusion. The issue
> that the mechanism does not let you trace some commands
> (scripts) raised on the list has stalled this topic branch.
> I'd either want people to agree that it is not a problem, or
> if they feel it is a problem, have a fix for that, before
> merging this to "next".
You probably allude to my comments, which were meant more as a hint to go
towards C, instead of scripts. There is a lot to be said about C, but the
fact is: if you have most of the core of git in C (if not all of it), you
have less problems (especially when integrating over _all_ platforms and
setups).
As it is, I am all for inclusion of GIT_TRACE support. If need be,
everybody can add GIT_TRACE support to the script she is debugging. And if
she's nice, she can send a patch to the list, too.
> - Auto configuration by Pasky and Jakub. This deserves a fresh
> paragraph.
I can see why some may want autoconf (or a clone of MPlayer's configure)
in git. But the fact is: on many platforms, the Makefile works out of the
box. Especially on cygwin, where _any_ shell script -- and autoconf
generated scripts in particular -- are slower than a dog's poo, it is
_soooo_ much better to be able to just say "make".
Please, please, _please_ keep autoconfiguration _strictly_ opt-in.
> Linus Torvalds:
> builtin "git prune"
FWIW, I read the code, too, and I agree it is ready for 'next'. After my
first read, I was curious why the parameter "object_array *p" was needed
for all the process_* functions. It turned out that add_object() needs
this. Just in case anybody else wonders.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH] Use configurable zlib compression level everywhere.
From: Johannes Schindelin @ 2006-07-08 2:10 UTC (permalink / raw)
To: David Lang; +Cc: Junio C Hamano, Joachim B Haga, git
In-Reply-To: <Pine.LNX.4.63.0607071451430.1836@qynat.qvtvafvgr.pbz>
Hi,
On Fri, 7 Jul 2006, David Lang wrote:
> On Mon, 3 Jul 2006, Junio C Hamano wrote:
>
> > * sha1write_compressed() in csum-file.c is for producing packs
> > and most of the things we compress there are deltas and less
> > compressible, so even when core.compression is set to high we
> > might be better off using faster compression.
>
> why would deltas have poor compression? I'd expect them to have about the same
> as the files they are deltas of (or slightly better due to the fact that the
> deta metainfo is highly repetitive)
Deltas should have poor compression by definition, because compression
tries to encode those parts of the file more efficiently, which do not
bear much information (think entropy).
If you have deltas which really make sense, they are almost _pure_
information, i.e. they do not contain much redundancy, as compared to real
files. So, the compression (which does not know anything about the
characteristics of deltas in particular) cannot take much redundancy out
of the delta. Therefore, the entropy is very high, and the compression
rate is low.
Hope this makes sense to you,
Dscho
^ permalink raw reply
* Re: [PATCH] Fix compilation
From: Johannes Schindelin @ 2006-07-08 2:04 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vbqs1w5d0.fsf@assigned-by-dhcp.cox.net>
Hi,
On Fri, 7 Jul 2006, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > Note also, that this patch relies on the perl scripts not
> > caring if an additional command is shifted into the 2nd line.
>
> How about doing something like this instead, then?
I will try as soon as possible. ATM my iBook is in my car, which I had to
leave at a friend's place, because said friend sensibly refused to let me
drive.
Ciao,
Dscho
^ permalink raw reply
* [PATCH] Fix several places where diff.renames in config can be problematic
From: Eric Wong @ 2006-07-08 1:58 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Martin Langhoff
In-Reply-To: <7v64i9zk0j.fsf@assigned-by-dhcp.cox.net>
git-cvsexportcommit.perl:
git-cvsserver.perl:
CVS can't handle renames, so we best not show them to
users.
templates/hooks--update:
replace diffstat calls with git diff --stat
There may be other places where users having diff.renames can be
problematic, too. diff.renames is opt-in, but maybe some users
have enabled it in their configs previously because it's been
in examples for a long time...
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
Junio C Hamano <junkio@cox.net> wrote:
> Junio C Hamano <junkio@cox.net> writes:
>
> > I am more worried about somebody who opts-in finds breakage of
> > commands that happen to internally use low-level diff machinery
> > and expect the diff machinery does not automagically do funny
> > rename detection without being told.
> > ...
> > That is why I said I do not want this at _that_ low level. I do
> > not have objections to have the configuration at a layer closer
> > to the UI, e.g. things in builtin-log.c and builtin-diff.c.
>
> Upon closer look I think the revision pruning code is OK. So
> let's cook this as is in "next" and see what happens.
Cool. I've found these that could be potential issues. There could be
more, and possibly many in 3rd-party scripts outside our control, too.
git-cvsexportcommit.perl | 8 +++++---
git-cvsserver.perl | 2 +-
templates/hooks--update | 4 ++--
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index d1051d0..09ff2cc 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -91,7 +91,8 @@ close MSG;
$? && die "Error extracting the commit message";
my (@afiles, @dfiles, @mfiles, @dirs);
-my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
+my @files = safe_pipe_capture('git-diff-tree','--no-renames','-r',
+ $parent, $commit);
#print @files;
$? && die "Error in git-diff-tree";
foreach my $f (@files) {
@@ -175,7 +176,8 @@ foreach my $d (@dirs) {
print "'Patching' binary files\n";
-my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
+my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree','--no-renames',
+ '-p', $parent, $commit));
@bfiles = map { chomp } @bfiles;
foreach my $f (@bfiles) {
# check that the file in cvs matches the "old" file
@@ -206,7 +208,7 @@ ## apply non-binary changes
my $fuzz = $opt_p ? 0 : 2;
print "Patching non-binary files\n";
-print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
+print `(git-diff-tree --no-renames -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
my $dirtypatch = 0;
if (($? >> 8) == 2) {
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 5ccca4f..ae878ea 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -2295,7 +2295,7 @@ sub update
if ( defined ( $lastpicked ) )
{
- my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
+ my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '--no-merges', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
while ( <FILELIST> )
{
unless ( /^:\d{6}\s+\d{3}(\d)\d{2}\s+[a-zA-Z0-9]{40}\s+([a-zA-Z0-9]{40})\s+(\w)\s+(.*)$/o )
diff --git a/templates/hooks--update b/templates/hooks--update
index d7a8f0a..76d5ac2 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -60,7 +60,7 @@ then
echo "Changes since $prev:"
git rev-list --pretty $prev..$3 | $short
echo ---
- git diff $prev..$3 | diffstat -p1
+ git diff --stat $prev..$3
echo ---
fi
;;
@@ -75,7 +75,7 @@ else
base=$(git-merge-base "$2" "$3")
case "$base" in
"$2")
- git diff "$3" "^$base" | diffstat -p1
+ git diff --stat "$3" "^$base"
echo
echo "New commits:"
;;
--
1.4.1.gc57e
^ permalink raw reply related
* What's in git.git
From: Junio C Hamano @ 2006-07-08 0:37 UTC (permalink / raw)
To: git
Many fixes came in since 1.4.1 was done. Please see the
short-log for what's new in "master".
In the "next" branch, these are cooking:
- Early parts of Perly Git by Petr Baudis with help from others
are merged to "next". Please report breakages before other
Perl scripts are converted to use this.
- "diff --text" by Stephan Feder. I think this one is correct
and should be very safe, but bugs happen to "obvious" code
too, so I have not merged it to "master" yet.
- Move git-svn by Eric Wong out of contrib to make it a
first-class citizen.
- "git-merge-tree" improvements by Linus. I may want to update
this to use our diff routines, but probably after OLS.
In "pu", there are a few more:
- An updated result minimization code in merge-base. I think
this is correct and is not too expensive. I'd like to run a
few more test on this and merge it to "next".
- Updated upload-pack to avoid letting the downloader to
traverse commits from a branch all the way down to root in
vain, when that root is knot known to upload-pack. This
needs another round of test with something like Linus's 2.6
repository and MIPS repository, which costs me quite a lot of
time so I am postponing it right now.
- Built-in git-prune by Linus. I have read the code and did
not spot problems, and it indeed is a lot faster. But this
is git-prune, so I am playing it a bit more cautiously than I
usually do.
- GIT_TRACE by Matthias Lederhofer. What it lets you do is
interesting (although I personally do not foresee myself
using it), and I am in favor of its inclusion. The issue
that the mechanism does not let you trace some commands
(scripts) raised on the list has stalled this topic branch.
I'd either want people to agree that it is not a problem, or
if they feel it is a problem, have a fix for that, before
merging this to "next".
- Auto configuration by Pasky and Jakub. This deserves a fresh
paragraph.
The first patch to use autoconf to build ./configure by Jakub
Narebski is in "pu". I am not against an autoconfiguration
mechanism to maintain config.mak.gen mechanically as an _option_,
and if we are going to have a ./configure script, it would be
better to avoid reinventing the tests autoconf people worked for
a long time to make robust and portable.
I hope further patches to this series will be added soon to
autodetect the following in our Makefile (from "pu"):
NO_D_INO_IN_DIRENT
NO_D_TYPE_IN_DIRENT
NO_STRCASESTR
NO_STRLCPY
NO_SETENV
USE_PIC
NEEDS_SSL_WITH_CRYPTO
NEEDS_LIBICONV
NEEDS_SOCKET
WITH_OWN_SUBPROCESS_PY
NO_IPV6
NO_SOCKADDR_STORAGE
NO_ICONV
These are not something you can change without replacing libc
(or installed Python) wholesale, so autodetection would be
always correct.
Also the following would be nice if autodetection worked for
most of the users with ./configure command line override:
CC
AR
TAR
INSTALL
SHELL_PATH
PERL_PATH
PYTHON_PATH
BASIC_CFLAGS mostly for -I include paths
BASIC_LDFLAGS mostly for -L library paths
ALL_LDFLAGS
NO_OPENSSL
MOZILLA_SHA1
PPC_SHA1
ARM_SHA1
NO_CURL
NO_EXPAT
CURLDIR
OPENSSLDIR
ICONVDIR
EXPATDIR (we do not have one, but I think it should be there
to allow -lexpat installed elsewhere just like
we have CURLDIR and OPENSSLDIR)
Note that I listed NO_OPENSSL, NO_CURL, and NO_EXPAT here not
with the first category, because you may not want to build git
with them even when you do have these libraries.
The directories our stuff will go is builder's policy, so I do
not think automating it is particularly useful, but I am not
opposed if ./configure allowed people to say --prefix=blah.
Because the autodetection of non-policy part is what I primarily
want from ./configure, people who run ./configure without giving
particular --prefix and friends to it should not be burned by
the choice ./configure makes by default. I presume they can
have their own customized prefix and friends in config.mak which
will be included after config.mak.gen so hopefully that is a
non-issue.
----------------------------------------------------------------
* The 'master' branch has these since the last announcement.
Dennis Stosberg:
gitweb: Declare global variables with "our"
Eric Wong:
Add git-instaweb, instantly browse the working repo with gitweb
instaweb: fix unportable ';' usage in sed
t8001-annotate: fix a bash-ism in this test
git-svn: avoid fetching files outside of the URL we're tracking
builtin-log: respect diff configuration options
Jakub Narebski:
send-email: format 2822 datestring ourselves.
Joachim B Haga:
Make zlib compression level configurable, and change default.
core.compression documentation formatting fix.
Johannes Schindelin:
refactor merge_bases() as preparation to libify merge-base
move get_merge_bases() to core lib.
Makefile: replace ugly and unportable sed invocation
Make git-fmt-merge-msg a builtin
Junio C Hamano:
Makefile: add framework to verify and bench sha1 implementations.
test-sha1: test hashing large buffer
t4013: add tests for diff/log family output options.
t4013: add more tests around -c and --cc
Fix some more diff options changes.
t4013 test updates for new output code.
combine-diff.c: type sanity.
format-patch: fix diff format option implementation
t4013: add format-patch tests.
t4013: note improvements brought by the new output code.
gitweb: optimize per-file history generation
t4013: add "diff" UI program tests.
builtin-diff: turn recursive on when defaulting to --patch format.
commit.c: do not redefine UNINTERESTING bit.
Makefile: tighten git-http-{fetch,push} dependencies
get_merge_bases: clean up even when there is no common commit.
revert clear-commit-marks for now.
boolean: accept yes and no as well
send-email: do not barf when Term::ReadLine does not like your terminal
t6200: fmt-merge-msg test.
git-grep: fix parsing of pathspec separator '--'
git-grep: fix exit code when we use external grep.
git-grep: use a bit more specific error messages.
Re-fix clear_commit_marks().
git-reset: complain and exit upon seeing an unknown parameter.
builtin-rev-parse.c: constness tightening
show-branch: match documentation and usage
rev-parse documentation: talk about range notation.
Linus Torvalds:
revision.c: fix "dense" under --remove-empty
Improve git-peek-remote
Luben Tuikov:
gitweb: Enable tree (directory) history display
Rene Scharfe:
Add get_merge_bases_clean()
Add '...' operator for revisions
Make clear_commit_marks() clean harder
Fold get_merge_bases_clean() into get_merge_bases()
rev-list: free commit_list in ... handler
Robin Rosenberg:
Empty author may be presented by svn as an empty string or a null value.
Ryan Anderson:
annotate: Support annotation of files on other revisions.
annotate: Correct most merge following to annotate correctly.
Santi Béjar:
Teach rev-parse the ... syntax.
Stephan Feder:
Do not drop data from '\0' until eol in patch output
Timo Hirvonen:
Merge with_raw, with_stat and summary variables to output_format
Make --raw option available for all diff commands
Set default diff output format after parsing command line
DIFF_FORMAT_RAW is not default anymore
Add msg_sep to diff_options
Don't xcalloc() struct diffstat_t
whatchanged: Default to DIFF_FORMAT_RAW
Print empty line between raw, stat, summary and patch
diff-tree: Use ---\n as a message separator
log --raw: Don't descend into subdirectories by default
Fix diff-tree -s
Unknown:
A better-scheduled PPC SHA-1 implementation.
Ville Skyttä:
Fix print-log and diff compatibility with recent vc versions
* The 'next' branch, in addition, has these.
Dennis Stosberg:
"test" in Solaris' /bin/sh does not support -e
Makefile fix for Solaris
Add possibility to pass CFLAGS and LDFLAGS specific to the perl subdir
Eric Wong:
git-svn: migrate out of contrib
git-svn: migrate out of contrib (follow-up)
diff.c: respect diff.renames config option
Jakub Narebski:
Allow INSTALL, bindir, mandir to be set in main Makefile
Rename man1 and man7 variables to man1dir and man7dir
Johannes Schindelin:
Git.xs: older perl do not know const char *
Makefile: export NO_SVN_TESTS
Junio C Hamano:
Perl interface: add build-time configuration to allow building with -fPIC
Perl interface: make testsuite work again.
perl: fix make clean
Git.pm: tentative fix to test the freshly built Git.pm
Perly Git: arrange include path settings properly.
Makefile: Set USE_PIC on x86-64
Perly git: work around buggy make implementations.
Git.pm: clean generated files.
Perly Git: make sure we do test the freshly built one.
INSTALL: a tip for running after building but without installing.
git-grep: boolean expression on pattern matching.
mailinfo: assume input is latin-1 on the header as we do for the body
diffcore-rename: try matching up renames without populating filespec first.
diff.c: --no-color to defeat diff.color configuration.
Update diff-options and config documentation.
git log -p --merge [[--] paths...]
Linus Torvalds:
xdiff: generate "anti-diffs" aka what is common to two files
Prepare "git-merge-tree" for future work
Improved three-way blob merging code
Pavel Roskin:
Fix probing for already installed Error.pm
Delete manuals if compiling without docs
Make perl interface a separate package
Petr Baudis:
Introduce Git.pm (v4)
Git.pm: Implement Git::exec_path()
Git.pm: Call external commands using execv_git_cmd()
Git.pm: Implement Git::version()
Add Error.pm to the distribution
Git.pm: Better error handling
Git.pm: Handle failed commands' output
Git.pm: Enhance the command_pipe() mechanism
Git.pm: Implement options for the command interface
Git.pm: Add support for subdirectories inside of working copies
Convert git-mv to use Git.pm
Git.pm: assorted build related fixes.
Git.pm: Try to support ActiveState output pipe
Git.pm: Swap hash_object() parameters
Git.pm: Fix Git->repository("/somewhere/totally/elsewhere")
Git.pm: Support for perl/ being built by a different compiler
Git.pm: Remove PerlIO usage from Git.xs
Git.pm: Avoid ppport.h
Git.pm: Don't #define around die
Use $GITPERLLIB instead of $RUNNING_GIT_TESTS and centralize @INC munging
Git.pm: Add config() method
Convert git-send-email to use Git.pm
Git.pm: Introduce ident() and ident_person() methods
Stephan Feder:
Teach --text option to diff
Teach diff -a as shorthand for --text
Add -a and --text to common diff options help
diff-options: Explain --text and -a
Timo Hirvonen:
--name-only, --name-status, --check and -s are mutually exclusive
* The 'pu' branch, in addition, has these.
A Large Angry SCM:
Additional merge-base tests (revised)
Jakub Narebski:
autoconf: Use autoconf to write installation directories to config.mak.autogen
Junio C Hamano:
merge-base: update the clean-up postprocessing
upload-pack: use object pointer not copy of sha1 to keep track of has/needs.
upload-pack: lift MAX_NEEDS and MAX_HAS limitation
upload-pack: minor clean-up in multi-ack logic
upload-pack: stop the other side when they have more roots than we do.
Work around sed and make interactions on the backslash at the end of line.
Linus Torvalds:
builtin "git prune"
Matthias Lederhofer:
GIT_TRACE: show which built-in/external commands are executed
Peter Baumann:
git-cvsexportcommit can't handle merge commits correctly
Petr Baudis:
Make it possible to set up libgit directly (instead of from the environment)
Git.pm: Introduce fast get_object() method
Convert git-annotate to use Git.pm
Timo Hirvonen:
Remove awkward compatibility warts
GIT_TRACE: fix a mixed declarations and code warning
^ permalink raw reply
* Re: [PATCH] Use configurable zlib compression level everywhere.
From: David Lang @ 2006-07-07 21:53 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Joachim B Haga, git
In-Reply-To: <7v4pxyscdt.fsf@assigned-by-dhcp.cox.net>
On Mon, 3 Jul 2006, Junio C Hamano wrote:
> * sha1write_compressed() in csum-file.c is for producing packs
> and most of the things we compress there are deltas and less
> compressible, so even when core.compression is set to high we
> might be better off using faster compression.
why would deltas have poor compression? I'd expect them to have about the same
as the files they are deltas of (or slightly better due to the fact that the
deta metainfo is highly repetitive)
David Lang
^ permalink raw reply
* Re: [PATCH 2, proof of concept] autoconf: Use %configure in git.spec, autoconf dependency only in rpm target
From: Jakub Narebski @ 2006-07-07 20:06 UTC (permalink / raw)
To: git
In-Reply-To: <1152159397.10415.29.camel@dv>
Pavel Roskin wrote:
> On Tue, 2006-07-04 at 16:09 +0200, Jakub Narebski wrote:
>
>> +Patch0: git-add-autoconf-configure.patch.gz
>
> I don't think we need patches in git.spec.in. Let's leave it to the
> actual distributions. If you have a problem with rpm, please submit the
> autoconf support for now and the rest will be cleaned up eventually.
> Besides, the "next" branch has different and potentially conflicting
> changes to git.spec.in for Git.pm support.
First, it is proof of concept patch, the concept being having autoconf
dependency _only_ in rpm target.
Second, it should be "Source1: autoconf-configure.tar.gz" or something like
that. Using "Patch0:" was an ugly hack.
That to say RPM builders use "PatchN:" for patches which didn't made
upstream; perhaps they make sources more compatibile with distribution RPM
is prepared for.
--
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git
^ 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