* [PATCH 5/8] Make git-update-index a builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 6 +++---
update-index.c => builtin-update-index.c | 19 ++++++++++++-------
builtin.h | 1 +
git.c | 3 ++-
4 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/Makefile b/Makefile
index 181255f..906fc0f 100644
--- a/Makefile
+++ b/Makefile
@@ -157,7 +157,7 @@ PROGRAMS = \
git-send-pack$X git-shell$X \
git-show-index$X git-ssh-fetch$X \
git-ssh-upload$X git-unpack-file$X \
- git-unpack-objects$X git-update-index$X git-update-server-info$X \
+ git-unpack-objects$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X \
git-update-ref$X git-symbolic-ref$X \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
@@ -170,7 +170,7 @@ BUILT_INS = git-log$X git-whatchanged$X
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
git-read-tree$X git-commit-tree$X git-write-tree$X \
- git-apply$X git-show-branch$X git-diff-files$X \
+ git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
# what 'all' will build and 'install' will install, in gitexecdir
@@ -221,7 +221,7 @@ BUILTIN_OBJS = \
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
- builtin-tar-tree.o builtin-upload-tar.o \
+ builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \
builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
diff --git a/update-index.c b/builtin-update-index.c
similarity index 97%
rename from update-index.c
rename to builtin-update-index.c
index fbccc4a..325cd09 100644
--- a/update-index.c
+++ b/builtin-update-index.c
@@ -8,6 +8,7 @@ #include "strbuf.h"
#include "quote.h"
#include "cache-tree.h"
#include "tree-walk.h"
+#include "builtin.h"
/*
* Default to not allowing changes to the list of files. The
@@ -186,8 +187,6 @@ static void chmod_path(int flip, const c
die("git-update-index: cannot chmod %cx '%s'", flip, path);
}
-static struct lock_file lock_file;
-
static void update_one(const char *path, const char *prefix, int prefix_length)
{
const char *p = prefix_path(prefix, prefix_length, path);
@@ -238,7 +237,7 @@ static void read_index_info(int line_ter
* (2) mode SP type SP sha1 TAB path
* The second format is to stuff git-ls-tree output
* into the index file.
- *
+ *
* (3) mode SP sha1 SP stage TAB path
* This format is to put higher order stages into the
* index file and matches git-ls-files --stage output.
@@ -477,7 +476,7 @@ static int do_reupdate(int ac, const cha
return 0;
}
-int main(int argc, const char **argv)
+int cmd_update_index(int argc, const char **argv, char **envp)
{
int i, newfd, entries, has_errors = 0, line_termination = '\n';
int allow_options = 1;
@@ -486,12 +485,16 @@ int main(int argc, const char **argv)
int prefix_length = prefix ? strlen(prefix) : 0;
char set_executable_bit = 0;
unsigned int refresh_flags = 0;
+ struct lock_file *lock_file;
git_config(git_default_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file());
+ /* We can't free this memory, it becomes part of a linked list parsed atexit() */
+ lock_file = xmalloc(sizeof(struct lock_file));
+
+ newfd = hold_lock_file_for_update(lock_file, get_index_file());
if (newfd < 0)
- die("unable to create new index file");
+ die("unable to create new cachefile");
entries = read_cache();
if (entries < 0)
@@ -645,9 +648,11 @@ int main(int argc, const char **argv)
finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ commit_lock_file(lock_file))
die("Unable to write new index file");
}
+ rollback_lock_file(lock_file);
+
return has_errors ? 1 : 0;
}
diff --git a/builtin.h b/builtin.h
index c934d7a..9ee5ea6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -47,6 +47,7 @@ extern int cmd_diff_stages(int argc, con
extern int cmd_diff_tree(int argc, const char **argv, char **envp);
extern int cmd_cat_file(int argc, const char **argv, char **envp);
extern int cmd_rev_parse(int argc, const char **argv, char **envp);
+extern int cmd_update_index(int argc, const char **argv, char **envp);
extern int cmd_write_tree(int argc, const char **argv, char **envp);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
diff --git a/git.c b/git.c
index 31196f5..4a931fb 100644
--- a/git.c
+++ b/git.c
@@ -182,7 +182,8 @@ static void handle_internal_command(int
{ "write-tree", cmd_write_tree },
{ "mailsplit", cmd_mailsplit },
{ "mailinfo", cmd_mailinfo },
- { "stripspace", cmd_stripspace }
+ { "stripspace", cmd_stripspace },
+ { "update-index", cmd_update_index }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 6/8] Make git-update-ref a builtin
From: Lukas Sandström @ 2006-06-13 20:22 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 7 ++++---
update-ref.c => builtin-update-ref.c | 5 ++++-
builtin.h | 1 +
git.c | 3 ++-
4 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
index 906fc0f..4b30ca0 100644
--- a/Makefile
+++ b/Makefile
@@ -159,11 +159,11 @@ PROGRAMS = \
git-ssh-upload$X git-unpack-file$X \
git-unpack-objects$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X \
- git-update-ref$X git-symbolic-ref$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
-BUILT_INS = git-log$X git-whatchanged$X git-show$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 \
git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
@@ -226,7 +226,8 @@ BUILTIN_OBJS = \
builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
- builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o
+ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
+ builtin-update-ref.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
diff --git a/update-ref.c b/builtin-update-ref.c
similarity index 89%
rename from update-ref.c
rename to builtin-update-ref.c
index a1e6bb9..00333c7 100644
--- a/update-ref.c
+++ b/builtin-update-ref.c
@@ -1,10 +1,11 @@
#include "cache.h"
#include "refs.h"
+#include "builtin.h"
static const char git_update_ref_usage[] =
"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
-int main(int argc, char **argv)
+int cmd_update_ref(int argc, const char **argv, char **envp)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
struct ref_lock *lock;
@@ -52,5 +53,7 @@ int main(int argc, char **argv)
return 1;
if (write_ref_sha1(lock, sha1, msg) < 0)
return 1;
+
+ /* write_ref_sha1 always unlocks the ref, no need to do it explicitly */
return 0;
}
diff --git a/builtin.h b/builtin.h
index 9ee5ea6..c1f3395 100644
--- a/builtin.h
+++ b/builtin.h
@@ -48,6 +48,7 @@ extern int cmd_diff_tree(int argc, const
extern int cmd_cat_file(int argc, const char **argv, char **envp);
extern int cmd_rev_parse(int argc, const char **argv, char **envp);
extern int cmd_update_index(int argc, const char **argv, char **envp);
+extern int cmd_update_ref(int argc, const char **argv, char **envp);
extern int cmd_write_tree(int argc, const char **argv, char **envp);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
diff --git a/git.c b/git.c
index 4a931fb..652e3c4 100644
--- a/git.c
+++ b/git.c
@@ -183,7 +183,8 @@ static void handle_internal_command(int
{ "mailsplit", cmd_mailsplit },
{ "mailinfo", cmd_mailinfo },
{ "stripspace", cmd_stripspace },
- { "update-index", cmd_update_index }
+ { "update-index", cmd_update_index },
+ { "update-ref", cmd_update_ref }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 7/8] Make it possible to call cmd_apply multiple times
From: Lukas Sandström @ 2006-06-13 20:22 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
* xmalloc a new struct lock_file each invocation.
* Don't die() if the patch doesn't apply.
* Initialize the global variables each invocation.
* Roll back the lock_file.
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
builtin-apply.c | 94 +++++++++++++++++++++++++++++++++++--------------------
1 files changed, 60 insertions(+), 34 deletions(-)
diff --git a/builtin-apply.c b/builtin-apply.c
index e113c74..a76c553 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -22,24 +22,13 @@ #include "builtin.h"
// --index updates the cache as well.
// --cached updates only the cache without ever touching the working tree.
//
-static const char *prefix;
-static int prefix_length = -1;
-static int newfd = -1;
-
-static int p_value = 1;
-static int allow_binary_replacement = 0;
-static int check_index = 0;
-static int write_index = 0;
-static int cached = 0;
-static int diffstat = 0;
-static int numstat = 0;
-static int summary = 0;
-static int check = 0;
-static int apply = 1;
-static int no_add = 0;
-static int show_index_info = 0;
-static int line_termination = '\n';
+static const char *prefix, *patch_input_file;
+static int prefix_length, newfd, p_value, allow_binary_replacement, check_index,
+ write_index, cached, diffstat, numstat, summary, check, apply, no_add,
+ show_index_info, line_termination, whitespace_error, squelch_whitespace_errors,
+ applied_after_stripping;
static unsigned long p_context = -1;
+
static const char apply_usage[] =
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
@@ -49,10 +38,31 @@ static enum whitespace_eol {
error_on_whitespace,
strip_whitespace,
} new_whitespace = warn_on_whitespace;
-static int whitespace_error = 0;
-static int squelch_whitespace_errors = 5;
-static int applied_after_stripping = 0;
-static const char *patch_input_file = NULL;
+
+static void setup_state()
+{
+ prefix = NULL;
+ prefix_length = -1;
+ newfd = -1;
+ p_value = 1;
+ allow_binary_replacement = 0;
+ check_index = 0;
+ write_index = 0;
+ cached = 0;
+ diffstat = 0;
+ numstat = 0;
+ summary = 0;
+ check = 0;
+ apply = 1;
+ no_add = 0;
+ show_index_info = 0;
+ line_termination = '\n';
+ p_context = -1;
+ whitespace_error = 0;
+ squelch_whitespace_errors = 5;
+ applied_after_stripping = 0;
+ patch_input_file = NULL;
+}
static void parse_whitespace_option(const char *option)
{
@@ -2072,7 +2082,7 @@ static void write_out_results(struct pat
}
}
-static struct lock_file lock_file;
+static struct lock_file *lock_file;
static struct excludes {
struct excludes *next;
@@ -2106,7 +2116,7 @@ static int apply_patch(int fd, const cha
patch_input_file = filename;
if (!buffer)
- return -1;
+ return -2;
offset = 0;
while (size > 0) {
struct patch *patch;
@@ -2134,7 +2144,7 @@ static int apply_patch(int fd, const cha
write_index = check_index && apply;
if (write_index && newfd < 0) {
- newfd = hold_lock_file_for_update(&lock_file,
+ newfd = hold_lock_file_for_update(lock_file,
get_index_file());
if (newfd < 0)
die("unable to create new index file");
@@ -2145,7 +2155,7 @@ static int apply_patch(int fd, const cha
}
if ((check || apply) && check_patch_list(list) < 0)
- exit(1);
+ return -1;
if (apply)
write_out_results(list, skipped_patch);
@@ -2175,20 +2185,27 @@ static int git_apply_config(const char *
return git_default_config(var, value);
}
-
int cmd_apply(int argc, const char **argv, char **envp)
{
- int i;
+ int i, ret = 0;
int read_stdin = 1;
const char *whitespace_option = NULL;
+ setup_state();
+
+ /* This memory can't be free()'d since it's needed atexit() */
+ lock_file = xmalloc(sizeof(struct lock_file));
+
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
char *end;
- int fd;
+ int fd, apply_status;
if (!strcmp(arg, "-")) {
- apply_patch(0, "<stdin>");
+ if (apply_patch(0, "<stdin>")) {
+ ret = 1;
+ goto err;
+ }
read_stdin = 0;
continue;
}
@@ -2281,12 +2298,18 @@ int cmd_apply(int argc, const char **arg
usage(apply_usage);
read_stdin = 0;
set_default_whitespace_mode(whitespace_option);
- apply_patch(fd, arg);
+ apply_status = apply_patch(fd, arg);
close(fd);
+ if (apply_status) {
+ ret = 1;
+ goto err;
+ }
}
set_default_whitespace_mode(whitespace_option);
- if (read_stdin)
- apply_patch(0, "<stdin>");
+ if (read_stdin && apply_patch(0, "<stdin>")) {
+ ret = 1;
+ goto err;
+ }
if (whitespace_error) {
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error) {
@@ -2316,9 +2339,12 @@ int cmd_apply(int argc, const char **arg
if (write_index) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ commit_lock_file(lock_file))
die("Unable to write new index file");
}
- return 0;
+err:
+ rollback_lock_file(lock_file);
+
+ return ret;
}
--
1.4.0
^ permalink raw reply related
* [PATCH 4/8] Make git-stripspace a builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 6 +++---
stripspace.c => builtin-stripspace.c | 16 +++++++++++-----
builtin.h | 6 ++++--
git.c | 3 ++-
4 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/Makefile b/Makefile
index e64d943..181255f 100644
--- a/Makefile
+++ b/Makefile
@@ -144,7 +144,7 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)
# The ones that do not have to link with lcrypto, lz nor xdiff.
SIMPLE_PROGRAMS = \
- git-stripspace$X git-daemon$X
+ git-daemon$X
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
@@ -165,7 +165,7 @@ PROGRAMS = \
BUILT_INS = git-log$X git-whatchanged$X git-show$X \
git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
- git-grep$X git-add$X git-rm$X git-rev-list$X \
+ git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
@@ -226,7 +226,7 @@ BUILTIN_OBJS = \
builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
- builtin-cat-file.o builtin-mailsplit.o
+ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
diff --git a/stripspace.c b/builtin-stripspace.c
similarity index 76%
rename from stripspace.c
rename to builtin-stripspace.c
index 65a6346..2ce1264 100644
--- a/stripspace.c
+++ b/builtin-stripspace.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include "builtin.h"
/*
* Remove empty lines from the beginning and end.
@@ -28,21 +29,21 @@ static int cleanup(char *line)
return 1;
}
-int main(int argc, char **argv)
+void stripspace(FILE *in, FILE *out)
{
int empties = -1;
int incomplete = 0;
char line[1024];
- while (fgets(line, sizeof(line), stdin)) {
+ while (fgets(line, sizeof(line), in)) {
incomplete = cleanup(line);
/* Not just an empty line? */
if (line[0] != '\n') {
if (empties > 0)
- putchar('\n');
+ fputc('\n', out);
empties = 0;
- fputs(line, stdout);
+ fputs(line, out);
continue;
}
if (empties < 0)
@@ -50,6 +51,11 @@ int main(int argc, char **argv)
empties++;
}
if (incomplete)
- putchar('\n');
+ fputc('\n', out);
+}
+
+int cmd_stripspace(int argc, const char **argv, char **envp)
+{
+ stripspace(stdin, stdout);
return 0;
}
diff --git a/builtin.h b/builtin.h
index 979e0cd..c934d7a 100644
--- a/builtin.h
+++ b/builtin.h
@@ -55,6 +55,8 @@ extern int cmd_mailsplit(int argc, const
extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
extern int cmd_mailinfo(int argc, const char **argv, char **envp);
-extern int mailinfo(FILE *in, FILE *out, int ks, char *encoding,
- const char *msg, const char *patch);
+extern int mailinfo(FILE *in, FILE *out, int ks, char *encoding, const char *msg, const char *patch);
+
+extern int cmd_stripspace(int argc, const char **argv, char **envp);
+extern void stripspace(FILE *in, FILE *out);
#endif
diff --git a/git.c b/git.c
index 1e216de..31196f5 100644
--- a/git.c
+++ b/git.c
@@ -181,7 +181,8 @@ static void handle_internal_command(int
{ "rev-parse", cmd_rev_parse },
{ "write-tree", cmd_write_tree },
{ "mailsplit", cmd_mailsplit },
- { "mailinfo", cmd_mailinfo }
+ { "mailinfo", cmd_mailinfo },
+ { "stripspace", cmd_stripspace }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 3/8] Make git-mailinfo a builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 12 ++---
mailinfo.c => builtin-mailinfo.c | 85 ++++++++++++++++++++++----------------
builtin.h | 6 +++
git.c | 3 +
4 files changed, 62 insertions(+), 44 deletions(-)
diff --git a/Makefile b/Makefile
index 7bee30d..e64d943 100644
--- a/Makefile
+++ b/Makefile
@@ -151,7 +151,7 @@ PROGRAMS = \
git-checkout-index$X git-clone-pack$X \
git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
- git-mailinfo$X git-merge-base$X \
+ git-merge-base$X \
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
git-peek-remote$X git-prune-packed$X git-receive-pack$X \
git-send-pack$X git-shell$X \
@@ -166,7 +166,7 @@ PROGRAMS = \
BUILT_INS = git-log$X git-whatchanged$X git-show$X \
git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
git-grep$X git-add$X git-rm$X git-rev-list$X \
- git-check-ref-format$X git-rev-parse$X \
+ git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
git-read-tree$X git-commit-tree$X git-write-tree$X \
@@ -223,7 +223,7 @@ BUILTIN_OBJS = \
builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
builtin-tar-tree.o builtin-upload-tar.o \
builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
- builtin-read-tree.o builtin-commit-tree.o \
+ builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
builtin-cat-file.o builtin-mailsplit.o
@@ -486,7 +486,7 @@ strip: $(PROGRAMS) git$X
git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)
$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
- $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
+ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) $(LIB_4_ICONV)
builtin-help.o: common-cmds.h
@@ -563,10 +563,6 @@ git-%$X: %.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIB_FILE) $(SIMPLE_LIB)
-git-mailinfo$X: mailinfo.o $(LIB_FILE)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
- $(LIB_FILE) $(SIMPLE_LIB) $(LIB_4_ICONV)
-
git-local-fetch$X: fetch.o
git-ssh-fetch$X: rsh.o fetch.o
git-ssh-upload$X: rsh.o
diff --git a/mailinfo.c b/builtin-mailinfo.c
similarity index 94%
rename from mailinfo.c
rename to builtin-mailinfo.c
index 5b6c215..6e30758 100644
--- a/mailinfo.c
+++ b/builtin-mailinfo.c
@@ -12,8 +12,9 @@ #include <iconv.h>
#endif
#include "git-compat-util.h"
#include "cache.h"
+#include "builtin.h"
-static FILE *cmitmsg, *patchfile;
+static FILE *cmitmsg, *patchfile, *fin, *fout;
static int keep_subject = 0;
static char *metainfo_charset = NULL;
@@ -49,7 +50,7 @@ static int bogus_from(char *line)
/* This is fallback, so do not bother if we already have an
* e-mail address.
- */
+ */
if (*email)
return 0;
@@ -311,12 +312,12 @@ static char *cleanup_subject(char *subje
if (remove <= len *2) {
subject = p+1;
continue;
- }
+ }
break;
}
return subject;
}
-}
+}
static void cleanup_space(char *buf)
{
@@ -638,7 +639,7 @@ static void handle_info(void)
cleanup_space(email);
cleanup_space(sub);
- printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
+ fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
name, email, sub, date);
}
@@ -675,7 +676,7 @@ static int handle_commit_msg(int *seen)
continue;
fputs(line, cmitmsg);
- } while (fgets(line, sizeof(line), stdin) != NULL);
+ } while (fgets(line, sizeof(line), fin) != NULL);
fclose(cmitmsg);
cmitmsg = NULL;
return 0;
@@ -696,7 +697,7 @@ static void handle_patch(void)
decode_transfer_encoding(line);
fputs(line, patchfile);
patch_lines++;
- } while (fgets(line, sizeof(line), stdin) != NULL);
+ } while (fgets(line, sizeof(line), fin) != NULL);
}
/* multipart boundary and transfer encoding are set up for us, and we
@@ -709,7 +710,7 @@ static int handle_multipart_one_part(int
{
int n = 0;
- while (fgets(line, sizeof(line), stdin) != NULL) {
+ while (fgets(line, sizeof(line), fin) != NULL) {
again:
n++;
if (is_multipart_boundary(line))
@@ -730,7 +731,7 @@ static void handle_multipart_body(void)
int part_num = 0;
/* Skip up to the first boundary */
- while (fgets(line, sizeof(line), stdin) != NULL)
+ while (fgets(line, sizeof(line), fin) != NULL)
if (is_multipart_boundary(line)) {
part_num = 1;
break;
@@ -739,7 +740,7 @@ static void handle_multipart_body(void)
return;
/* We are on boundary line. Start slurping the subhead. */
while (1) {
- int hdr = read_one_header_line(line, sizeof(line), stdin);
+ int hdr = read_one_header_line(line, sizeof(line), fin);
if (!hdr) {
if (handle_multipart_one_part(&seen) < 0)
return;
@@ -762,7 +763,7 @@ static void handle_body(void)
{
int seen = 0;
- if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
+ if (line[0] || fgets(line, sizeof(line), fin) != NULL) {
handle_commit_msg(&seen);
handle_patch();
}
@@ -773,10 +774,45 @@ static void handle_body(void)
}
}
+int mailinfo(FILE *in, FILE *out, int ks, char *encoding,
+ const char *msg, const char *patch)
+{
+ keep_subject = ks;
+ metainfo_charset = encoding;
+ fin = in;
+ fout = out;
+
+ cmitmsg = fopen(msg, "w");
+ if (!cmitmsg) {
+ perror(msg);
+ return -1;
+ }
+ patchfile = fopen(patch, "w");
+ if (!patchfile) {
+ perror(patch);
+ fclose(cmitmsg);
+ return -1;
+ }
+ while (1) {
+ int hdr = read_one_header_line(line, sizeof(line), fin);
+ if (!hdr) {
+ if (multipart_boundary[0])
+ handle_multipart_body();
+ else
+ handle_body();
+ handle_info();
+ break;
+ }
+ check_header_line(line);
+ }
+
+ return 0;
+}
+
static const char mailinfo_usage[] =
"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
-int main(int argc, char **argv)
+int cmd_mailinfo(int argc, const char **argv, char **envp)
{
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
@@ -797,27 +833,6 @@ int main(int argc, char **argv)
if (argc != 3)
usage(mailinfo_usage);
- cmitmsg = fopen(argv[1], "w");
- if (!cmitmsg) {
- perror(argv[1]);
- exit(1);
- }
- patchfile = fopen(argv[2], "w");
- if (!patchfile) {
- perror(argv[2]);
- exit(1);
- }
- while (1) {
- int hdr = read_one_header_line(line, sizeof(line), stdin);
- if (!hdr) {
- if (multipart_boundary[0])
- handle_multipart_body();
- else
- handle_body();
- handle_info();
- break;
- }
- check_header_line(line);
- }
- return 0;
+
+ return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
}
diff --git a/builtin.h b/builtin.h
index 92e1e1b..979e0cd 100644
--- a/builtin.h
+++ b/builtin.h
@@ -1,6 +1,8 @@
#ifndef BUILTIN_H
#define BUILTIN_H
+#include <stdio.h>
+
#ifndef PATH_MAX
# define PATH_MAX 4096
#endif
@@ -51,4 +53,8 @@ extern int write_tree(unsigned char *sha
extern int cmd_mailsplit(int argc, const char **argv, char **envp);
extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
+
+extern int cmd_mailinfo(int argc, const char **argv, char **envp);
+extern int mailinfo(FILE *in, FILE *out, int ks, char *encoding,
+ const char *msg, const char *patch);
#endif
diff --git a/git.c b/git.c
index 3264c65..1e216de 100644
--- a/git.c
+++ b/git.c
@@ -180,7 +180,8 @@ static void handle_internal_command(int
{ "cat-file", cmd_cat_file },
{ "rev-parse", cmd_rev_parse },
{ "write-tree", cmd_write_tree },
- { "mailsplit", cmd_mailsplit }
+ { "mailsplit", cmd_mailsplit },
+ { "mailinfo", cmd_mailinfo }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 2/8] Make git-mailsplit a builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 5 +--
mailsplit.c => builtin-mailsplit.c | 71 +++++++++++++++++++++---------------
builtin.h | 2 +
git.c | 3 +-
4 files changed, 48 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
index 37b5e40..7bee30d 100644
--- a/Makefile
+++ b/Makefile
@@ -144,7 +144,6 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)
# The ones that do not have to link with lcrypto, lz nor xdiff.
SIMPLE_PROGRAMS = \
- git-mailsplit$X \
git-stripspace$X git-daemon$X
# ... and all the rest that could be moved out of bindir to gitexecdir
@@ -165,7 +164,7 @@ PROGRAMS = \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
BUILT_INS = git-log$X git-whatchanged$X git-show$X \
- git-count-objects$X git-diff$X git-push$X \
+ git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
git-grep$X git-add$X git-rm$X git-rev-list$X \
git-check-ref-format$X git-rev-parse$X \
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
@@ -227,7 +226,7 @@ BUILTIN_OBJS = \
builtin-read-tree.o builtin-commit-tree.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
- builtin-cat-file.o
+ builtin-cat-file.o builtin-mailsplit.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
diff --git a/mailsplit.c b/builtin-mailsplit.c
similarity index 85%
rename from mailsplit.c
rename to builtin-mailsplit.c
index 70a569c..e2a0058 100644
--- a/mailsplit.c
+++ b/builtin-mailsplit.c
@@ -12,6 +12,7 @@ #include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include "cache.h"
+#include "builtin.h"
static const char git_mailsplit_usage[] =
"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
@@ -102,14 +103,48 @@ static int split_one(FILE *mbox, const c
exit(1);
}
-int main(int argc, const char **argv)
+int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)
{
- int nr = 0, nr_prec = 4;
+ char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));
+ int ret = -1;
+
+ while (*mbox) {
+ const char *file = *mbox++;
+ FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+ int file_done = 0;
+
+ if ( !f ) {
+ error("cannot open mbox %s", file);
+ goto out;
+ }
+
+ if (fgets(buf, sizeof(buf), f) == NULL) {
+ if (f == stdin)
+ break; /* empty stdin is OK */
+ error("cannot read mbox %s", file);
+ goto out;
+ }
+
+ while (!file_done) {
+ sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ file_done = split_one(f, name, allow_bare);
+ }
+
+ if (f != stdin)
+ fclose(f);
+ }
+ ret = skip;
+out:
+ free(name);
+ return ret;
+}
+int cmd_mailsplit(int argc, const char **argv, char **envp)
+{
+ int nr = 0, nr_prec = 4, ret;
int allow_bare = 0;
const char *dir = NULL;
const char **argp;
static const char *stdin_only[] = { "-", NULL };
- char *name;
for (argp = argv+1; *argp; argp++) {
const char *arg = *argp;
@@ -158,31 +193,9 @@ int main(int argc, const char **argv)
argp = stdin_only;
}
- name = xmalloc(strlen(dir) + 2 + 3 * sizeof(nr));
-
- while (*argp) {
- const char *file = *argp++;
- FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
- int file_done = 0;
-
- if ( !f )
- die ("cannot open mbox %s", file);
-
- if (fgets(buf, sizeof(buf), f) == NULL) {
- if (f == stdin)
- break; /* empty stdin is OK */
- die("cannot read mbox %s", file);
- }
-
- while (!file_done) {
- sprintf(name, "%s/%0*d", dir, nr_prec, ++nr);
- file_done = split_one(f, name, allow_bare);
- }
-
- if (f != stdin)
- fclose(f);
- }
+ ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);
+ if (ret != -1)
+ printf("%d\n", ret);
- printf("%d\n", nr);
- return 0;
+ return ret == -1;
}
diff --git a/builtin.h b/builtin.h
index 885422e..92e1e1b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -49,4 +49,6 @@ extern int cmd_rev_parse(int argc, const
extern int cmd_write_tree(int argc, const char **argv, char **envp);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+extern int cmd_mailsplit(int argc, const char **argv, char **envp);
+extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
#endif
diff --git a/git.c b/git.c
index 5868fb9..3264c65 100644
--- a/git.c
+++ b/git.c
@@ -179,7 +179,8 @@ static void handle_internal_command(int
{ "diff-tree", cmd_diff_tree },
{ "cat-file", cmd_cat_file },
{ "rev-parse", cmd_rev_parse },
- { "write-tree", cmd_write_tree }
+ { "write-tree", cmd_write_tree },
+ { "mailsplit", cmd_mailsplit }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 1/8] Make git-write-tree a builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
In-Reply-To: <448EF791.7070504@etek.chalmers.se>
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
---
Makefile | 6 ++-
write-tree.c => builtin-write-tree.c | 68 +++++++++++++++++++++-------------
builtin.h | 3 ++
git.c | 3 +-
4 files changed, 50 insertions(+), 30 deletions(-)
diff --git a/Makefile b/Makefile
index 2a1e639..37b5e40 100644
--- a/Makefile
+++ b/Makefile
@@ -159,7 +159,7 @@ PROGRAMS = \
git-show-index$X git-ssh-fetch$X \
git-ssh-upload$X git-unpack-file$X \
git-unpack-objects$X git-update-index$X git-update-server-info$X \
- git-upload-pack$X git-verify-pack$X git-write-tree$X \
+ git-upload-pack$X git-verify-pack$X \
git-update-ref$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
@@ -170,7 +170,7 @@ BUILT_INS = git-log$X git-whatchanged$X
git-check-ref-format$X git-rev-parse$X \
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
- git-read-tree$X git-commit-tree$X \
+ git-read-tree$X git-commit-tree$X git-write-tree$X \
git-apply$X git-show-branch$X git-diff-files$X \
git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
@@ -223,7 +223,7 @@ BUILTIN_OBJS = \
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
builtin-tar-tree.o builtin-upload-tar.o \
- builtin-ls-files.o builtin-ls-tree.o \
+ builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
builtin-read-tree.o builtin-commit-tree.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
diff --git a/write-tree.c b/builtin-write-tree.c
similarity index 66%
rename from write-tree.c
rename to builtin-write-tree.c
index bd07da6..c3aac36 100644
--- a/write-tree.c
+++ b/builtin-write-tree.c
@@ -3,41 +3,24 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
+#include "builtin.h"
#include "cache.h"
#include "tree.h"
#include "cache-tree.h"
-static int missing_ok = 0;
-static char *prefix = NULL;
-
static const char write_tree_usage[] =
"git-write-tree [--missing-ok] [--prefix=<prefix>/]";
-static struct lock_file lock_file;
-
-int main(int argc, char **argv)
+int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
{
int entries, was_valid, newfd;
- setup_git_directory();
+ /* We can't free this memory, it becomes part of a linked list parsed atexit() */
+ struct lock_file *lock_file = xmalloc(sizeof(struct lock_file));
- newfd = hold_lock_file_for_update(&lock_file, get_index_file());
- entries = read_cache();
-
- while (1 < argc) {
- char *arg = argv[1];
- if (!strcmp(arg, "--missing-ok"))
- missing_ok = 1;
- else if (!strncmp(arg, "--prefix=", 9))
- prefix = arg + 9;
- else
- die(write_tree_usage);
- argc--; argv++;
- }
-
- if (argc > 2)
- die("too many options");
+ newfd = hold_lock_file_for_update(lock_file, get_index_file());
+ entries = read_cache();
if (entries < 0)
die("git-write-tree: error reading cache");
@@ -45,6 +28,7 @@ int main(int argc, char **argv)
active_cache_tree = cache_tree();
was_valid = cache_tree_fully_valid(active_cache_tree);
+
if (!was_valid) {
if (cache_tree_update(active_cache_tree,
active_cache, active_nr,
@@ -52,7 +36,7 @@ int main(int argc, char **argv)
die("git-write-tree: error building trees");
if (0 <= newfd) {
if (!write_cache(newfd, active_cache, active_nr))
- commit_lock_file(&lock_file);
+ commit_lock_file(lock_file);
}
/* Not being able to write is fine -- we are only interested
* in updating the cache-tree part, and if the next caller
@@ -61,12 +45,44 @@ int main(int argc, char **argv)
* performance penalty and not a big deal.
*/
}
+
if (prefix) {
struct cache_tree *subtree =
cache_tree_find(active_cache_tree, prefix);
- printf("%s\n", sha1_to_hex(subtree->sha1));
+ memcpy(sha1, subtree->sha1, 20);
}
else
- printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
+ memcpy(sha1, active_cache_tree->sha1, 20);
+
+ rollback_lock_file(lock_file);
+
return 0;
}
+
+int cmd_write_tree(int argc, const char **argv, char **envp)
+{
+ int missing_ok = 0, ret;
+ const char *prefix = NULL;
+ unsigned char sha1[20];
+
+ setup_git_directory();
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--missing-ok"))
+ missing_ok = 1;
+ else if (!strncmp(arg, "--prefix=", 9))
+ prefix = arg + 9;
+ else
+ die(write_tree_usage);
+ argc--; argv++;
+ }
+
+ if (argc > 2)
+ die("too many options");
+
+ ret = write_tree(sha1, missing_ok, prefix);
+ printf("%s\n", sha1_to_hex(sha1));
+
+ return ret;
+}
diff --git a/builtin.h b/builtin.h
index b9f36be..885422e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -46,4 +46,7 @@ extern int cmd_diff_tree(int argc, const
extern int cmd_cat_file(int argc, const char **argv, char **envp);
extern int cmd_rev_parse(int argc, const char **argv, char **envp);
+extern int cmd_write_tree(int argc, const char **argv, char **envp);
+extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+
#endif
diff --git a/git.c b/git.c
index 9469d44..5868fb9 100644
--- a/git.c
+++ b/git.c
@@ -178,7 +178,8 @@ static void handle_internal_command(int
{ "diff-stages", cmd_diff_stages },
{ "diff-tree", cmd_diff_tree },
{ "cat-file", cmd_cat_file },
- { "rev-parse", cmd_rev_parse }
+ { "rev-parse", cmd_rev_parse },
+ { "write-tree", cmd_write_tree }
};
int i;
--
1.4.0
^ permalink raw reply related
* [PATCH 0/8] Make a couple of commands builtin
From: Lukas Sandström @ 2006-06-13 20:21 UTC (permalink / raw)
To: Junio C Hamano, Git Mailing List; +Cc: Lukas Sandström
This patchseries has the ultimate goal of making
git-am a builtin.
The version of git-am I'm sending out makes quite heavy
use of system(), but I think that can be worked around.
I just haven't figured out how, yet.
/Lukas
^ permalink raw reply
* Re: git-cvsimport doesn't quite work, wrt branches
From: Keith Packard @ 2006-06-13 18:46 UTC (permalink / raw)
To: Linus Torvalds
Cc: keithp, Jim Meyering, Git Mailing List, Matthias Urlichs,
Yann Dirson, Pavel Roskin
In-Reply-To: <Pine.LNX.4.64.0606131008470.5498@g5.osdl.org>
[-- Attachment #1: Type: text/plain, Size: 542 bytes --]
On Tue, 2006-06-13 at 10:20 -0700, Linus Torvalds wrote:
> Well, it's a cvsps problem.
>
> Big surprise.
Yeah, we've got
git-cvsimport
cvsps
cvs rlog
,v files
cvs rlog is designed to 'represent' the history of the repository to
users. Cvsps was built as a software analysis tool, and is used by
putative software engineering researchers. Basing a supposedly lossless
repository conversion system on this pair seems foolish to me,
notwithstanding the heroic efforts to make it work.
--
keith.packard@intel.com
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: Thoughts on adding another hook to git
From: Junio C Hamano @ 2006-06-13 18:41 UTC (permalink / raw)
To: David Kowis; +Cc: git
In-Reply-To: <448DB201.5090208@shlrm.org>
David Kowis <dkowis@shlrm.org> writes:
> I'd like to be able to modify the commit message before it ends up in
> the $EDITOR. This is a fairly trivial thing to implement:
> Call ${GIT_DIR}/hooks/pre-editor on COMMIT_MESSAGE before opening it in
> $EDITOR.
Three random thoughts.
- pre-editor is too generic a name because before making a
commit is not the only place we give you $EDITOR (both am and
tag do EDITOR thing IIRC). So the hook name must be more
specific to the commit codepath (otherwise your pre-editor
hook needs to be able to tell which codepath called it).
- git-commit gives you EDITOR when you are making a merge, and
you would probably want to keep the default merge commit
message without the prefixed directory thing. You probably
do not want to do this while doing --amend either.
- it might make sense to have a "commit template" that is used
when making a non-merge commit afresh (i.e. without -F, -m,
-c or -C to specify messages), instead of a hook script.
^ permalink raw reply
* [PATCH 7/13] git-svn: add --repack and --repack-flags= options
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
This should help keep disk usage sane for large imports.
--repack takes an optional argument for the interval, it
defaults to 1000 if no argument is specified.
Arguments to --repack-flags are passed directly to git-repack.
No arguments are passed by default.
Idea stolen from git-cvsimport :)
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 18 +++++++++++++++++-
1 files changed, 17 insertions(+), 1 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index a243060..a04cf1d 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -34,6 +34,7 @@ my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity,
+ $_repack, $_repack_nr, $_repack_flags,
$_version, $_upgrade, $_authors, $_branch_all_refs);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
@@ -42,7 +43,9 @@ my @repo_path_split_cache;
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'branch|b=s' => \@_branch_from,
'branch-all-refs|B' => \$_branch_all_refs,
- 'authors-file|A=s' => \$_authors );
+ 'authors-file|A=s' => \$_authors,
+ 'repack:i' => \$_repack,
+ 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
@@ -82,6 +85,7 @@ GetOptions(%opts, 'help|H|h' => \$_help,
'version|V' => \$_version,
'id|i=s' => \$GIT_SVN) or exit 1;
+set_default_vals();
usage(0) if $_help;
version() if $_version;
usage(1) unless defined $cmd;
@@ -1120,6 +1124,10 @@ sub git_commit {
sys(@update_ref);
sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit);
print "r$log_msg->{revision} = $commit\n";
+ if ($_repack && (--$_repack_nr == 0)) {
+ $_repack_nr = $_repack;
+ sys("git repack $_repack_flags");
+ }
return $commit;
}
@@ -1362,6 +1370,14 @@ sub read_repo_config {
}
}
+sub set_default_vals {
+ if (defined $_repack) {
+ $_repack = 1000 if ($_repack <= 0);
+ $_repack_nr = $_repack;
+ $_repack_flags ||= '';
+ }
+}
+
__END__
Data structures:
--
1.4.0
^ permalink raw reply related
* [PATCH 5/13] git-svn: Move all git-svn-related paths into $GIT_DIR/svn
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
Since GIT_SVN_ID usage is probably going to become more
widespread <evil grin>, we won't run the chance of somebody
having a GIT_SVN_ID name that conflicts with one of the default
directories that already exist in $GIT_DIR (branches/tags).
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 99 ++++++++++++++++++++++++++++++++------
contrib/git-svn/t/lib-git-svn.sh | 2 -
2 files changed, 85 insertions(+), 16 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 54f3d63..2dce4e7 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -6,7 +6,7 @@ use strict;
use vars qw/ $AUTHOR $VERSION
$SVN_URL $SVN_INFO $SVN_WC $SVN_UUID
$GIT_SVN_INDEX $GIT_SVN
- $GIT_DIR $REV_DIR/;
+ $GIT_DIR $REV_DIR $GIT_SVN_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
$VERSION = '1.1.0-pre';
@@ -37,6 +37,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$
$_version, $_upgrade, $_authors, $_branch_all_refs);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
+my @repo_path_split_cache;
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'branch|b=s' => \@_branch_from,
@@ -100,10 +101,11 @@ GetOptions(%opts, 'help|H|h' => \$_help,
'id|i=s' => \$GIT_SVN) or exit 1;
$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
-$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
+$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN";
+$GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
$SVN_URL = undef;
-$REV_DIR = "$GIT_DIR/$GIT_SVN/revs";
-$SVN_WC = "$GIT_DIR/$GIT_SVN/tree";
+$REV_DIR = "$GIT_SVN_DIR/revs";
+$SVN_WC = "$GIT_SVN_DIR/tree";
usage(0) if $_help;
version() if $_version;
@@ -111,6 +113,7 @@ usage(1) unless defined $cmd;
load_authors() if $_authors;
load_all_refs() if $_branch_all_refs;
svn_compat_check();
+migration_check() unless $cmd eq 'init';
$cmd{$cmd}->[0]->(@ARGV);
exit 0;
@@ -200,7 +203,7 @@ sub rebuild {
$latest = $rev;
}
assert_revision_eq_or_unknown($rev, $c);
- sys('git-update-ref',"$GIT_SVN/revs/$rev",$c);
+ sys('git-update-ref',"svn/$GIT_SVN/revs/$rev",$c);
$newest_rev = $rev if ($rev > $newest_rev);
}
close $rev_list or croak $?;
@@ -241,7 +244,7 @@ sub init {
sub fetch {
my (@parents) = @_;
check_upgrade_needed();
- $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
+ $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL);
unless ($_revision) {
$_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD';
@@ -350,7 +353,7 @@ sub show_ignore {
chomp(my @excludes = (<$fh>));
close $fh or croak $!;
- $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
+ $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
chdir $SVN_WC or croak $!;
my %ign;
File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
@@ -374,7 +377,44 @@ sub read_uuid {
my $info = shift || svn_info('.');
$SVN_UUID = $info->{'Repository UUID'} or
croak "Repository UUID unreadable\n";
- s_to_file($SVN_UUID,"$GIT_DIR/$GIT_SVN/info/uuid");
+ s_to_file($SVN_UUID,"$GIT_SVN_DIR/info/uuid");
+}
+
+sub quiet_run {
+ my $pid = fork;
+ defined $pid or croak $!;
+ if (!$pid) {
+ open my $null, '>', '/dev/null' or croak $!;
+ open STDERR, '>&', $null or croak $!;
+ open STDOUT, '>&', $null or croak $!;
+ exec @_ or croak $!;
+ }
+ waitpid $pid, 0;
+ return $?;
+}
+
+sub repo_path_split {
+ my $full_url = shift;
+ $full_url =~ s#/+$##;
+
+ foreach (@repo_path_split_cache) {
+ if ($full_url =~ s#$_##) {
+ my $u = $1;
+ $full_url =~ s#^/+##;
+ return ($u, $full_url);
+ }
+ }
+
+ my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
+ $path =~ s#^/+##;
+ my @paths = split(m#/+#, $path);
+
+ while (quiet_run(qw/svn ls --non-interactive/, $url)) {
+ my $n = shift @paths || last;
+ $url .= "/$n";
+ }
+ push @repo_path_split_cache, qr/^(\Q$url\E)/;
+ return ($url, $path);
}
sub setup_git_svn {
@@ -382,14 +422,17 @@ sub setup_git_svn {
unless (-d $GIT_DIR) {
croak "GIT_DIR=$GIT_DIR does not exist!\n";
}
- mkpath(["$GIT_DIR/$GIT_SVN"]);
- mkpath(["$GIT_DIR/$GIT_SVN/info"]);
+ mkpath([$GIT_SVN_DIR]);
+ mkpath(["$GIT_SVN_DIR/info"]);
mkpath([$REV_DIR]);
- s_to_file($SVN_URL,"$GIT_DIR/$GIT_SVN/info/url");
+ s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url");
- open my $fd, '>>', "$GIT_DIR/$GIT_SVN/info/exclude" or croak $!;
+ open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!;
print $fd '.svn',"\n";
close $fd or croak $!;
+ my ($url, $path) = repo_path_split($SVN_URL);
+ s_to_file($url, "$GIT_SVN_DIR/info/repo_url");
+ s_to_file($path, "$GIT_SVN_DIR/info/repo_path");
}
sub assert_svn_wc_clean {
@@ -688,7 +731,7 @@ sub handle_rmdir {
sub svn_commit_tree {
my ($svn_rev, $commit) = @_;
- my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$";
+ my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
my %log_msg = ( msg => '' );
open my $msg, '>', $commit_msg or croak $!;
@@ -965,7 +1008,7 @@ sub index_changes {
'remove',
$no_text_base);
do_update_index([qw/git-ls-files -z --others/,
- "--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"],
+ "--exclude-from=$GIT_SVN_DIR/info/exclude"],
'add',
$no_text_base);
}
@@ -1097,7 +1140,7 @@ sub git_commit {
push @update_ref, $primary_parent unless $?;
}
sys(@update_ref);
- sys('git-update-ref',"$GIT_SVN/revs/$log_msg->{revision}",$commit);
+ sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit);
print "r$log_msg->{revision} = $commit\n";
return $commit;
}
@@ -1283,6 +1326,32 @@ sub svn_propget_base {
return safe_qx(qw/svn propget/, $p, $f);
}
+sub migration_check {
+ return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR);
+ print "Upgrading repository...\n";
+ unless (-d "$GIT_DIR/svn") {
+ mkdir "$GIT_DIR/svn" or croak $!;
+ }
+ print "Data from a previous version of git-svn exists, but\n\t",
+ "$GIT_SVN_DIR\n\t(required for this version ",
+ "($VERSION) of git-svn) does not.\n";
+
+ foreach my $x (`git-rev-parse --symbolic --all`) {
+ next unless $x =~ s#^refs/remotes/##;
+ chomp $x;
+ next unless -f "$GIT_DIR/$x/info/url";
+ my $u = eval { file_to_s("$GIT_DIR/$x/info/url") };
+ next unless $u;
+ my $dn = dirname("$GIT_DIR/svn/$x");
+ mkpath([$dn]) unless -d $dn;
+ rename "$GIT_DIR/$x", "$GIT_DIR/svn/$x" or croak "$!: $x";
+ my ($url, $path) = repo_path_split($u);
+ s_to_file($url, "$GIT_DIR/svn/$x/info/repo_url");
+ s_to_file($path, "$GIT_DIR/svn/$x/info/repo_path");
+ }
+ print "Done upgrading.\n";
+}
+
__END__
Data structures:
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
index a98e9d1..58408a6 100644
--- a/contrib/git-svn/t/lib-git-svn.sh
+++ b/contrib/git-svn/t/lib-git-svn.sh
@@ -10,7 +10,7 @@ fi
. ./test-lib.sh
GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/git-svn
+GIT_SVN_DIR=$GIT_DIR/svn/git-svn
SVN_TREE=$GIT_SVN_DIR/tree
svnadmin >/dev/null 2>&1
--
1.4.0
^ permalink raw reply related
* [PATCH 9/13] git-svn: add some functionality to better support branches in svn
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
New commands:
graft-branches - The most interesting command of the bunch. It
detects branches in SVN via various techniques (currently
regexes and file copies). It can be later extended to handle
svk and other properties people may use to track merges in svk.
Basically, merge tracking is not standardized at all in the SVN
world, and git grafts are perfect for dealing with this
situation.
Existing branch support (via tree matches) is only handled at
fetch time.
The following tow were originally implemented as shell scripts
several months ago, but I just decided to streamline things a
bit and added them to the main script.
multi-init - supports git-svnimport-like command-line syntax for
importing repositories that are layed out as recommended by the
SVN folks. This is a bit more tolerant than the git-svnimport
command-line syntax and doesn't require the user to figure out
where the repository URL ends and where the repository path
begins.
multi-fetch - runs fetch on all known SVN branches we're
tracking. This will NOT discover new branches (unlike
git-svnimport), so multi-init will need to be re-run (it's
idempotent).
Consider these three to be auxilliary commands (like
show-ignore, and rebuild) so their behavior won't receive as
much testing or scrutiny as the core commands (fetch and
commit).
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 429 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 424 insertions(+), 5 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index d8f103e..d5c7e47 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -35,8 +35,8 @@ my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity,
$_repack, $_repack_nr, $_repack_flags,
- $_template, $_shared,
- $_version, $_upgrade, $_authors, $_branch_all_refs);
+ $_template, $_shared, $_no_default_regex, $_no_graft_copy,
+ $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@@ -48,6 +48,12 @@ my %fc_opts = ( 'no-ignore-externals' =>
'repack:i' => \$_repack,
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
+my ($_trunk, $_tags, $_branches);
+my %multi_opts = ( 'trunk|T=s' => \$_trunk,
+ 'tags|t=s' => \$_tags,
+ 'branches|b=s' => \$_branches );
+my %init_opts = ( 'template=s' => \$_template, 'shared' => \$_shared );
+
# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
@@ -56,8 +62,7 @@ my %cmd = (
{ 'revision|r=s' => \$_revision, %fc_opts } ],
init => [ \&init, "Initialize a repo for tracking" .
" (requires URL argument)",
- { 'template=s' => \$_template,
- 'shared' => \$_shared } ],
+ \%init_opts ],
commit => [ \&commit, "Commit git revisions to SVN",
{ 'stdin|' => \$_stdin,
'edit|e' => \$_edit,
@@ -71,7 +76,19 @@ my %cmd = (
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)",
{ 'no-ignore-externals' => \$_no_ignore_ext,
'upgrade' => \$_upgrade } ],
+ 'graft-branches' => [ \&graft_branches,
+ 'Detect merges/branches from already imported history',
+ { 'merge-rx|m' => \@_opt_m,
+ 'no-default-regex' => \$_no_default_regex,
+ 'no-graft-copy' => \$_no_graft_copy } ],
+ 'multi-init' => [ \&multi_init,
+ 'Initialize multiple trees (like git-svnimport)',
+ { %multi_opts, %fc_opts } ],
+ 'multi-fetch' => [ \&multi_fetch,
+ 'Fetch multiple trees (like git-svnimport)',
+ \%fc_opts ],
);
+
my $cmd;
for (my $i = 0; $i < @ARGV; $i++) {
if (defined $cmd{$ARGV[$i]}) {
@@ -96,7 +113,7 @@ init_vars();
load_authors() if $_authors;
load_all_refs() if $_branch_all_refs;
svn_compat_check();
-migration_check() unless $cmd eq 'init';
+migration_check() unless $cmd =~ /^(?:init|multi-init)$/;
$cmd{$cmd}->[0]->(@ARGV);
exit 0;
@@ -219,6 +236,7 @@ when you have upgraded your tools and ha
sub init {
$SVN_URL = shift or die "SVN repository location required " .
"as a command-line argument\n";
+ $SVN_URL =~ s!/+$!!; # strip trailing slash
unless (-d $GIT_DIR) {
my @init_db = ('git-init-db');
push @init_db, "--template=$_template" if defined $_template;
@@ -358,8 +376,283 @@ sub show_ignore {
}
}
+sub graft_branches {
+ my $gr_file = "$GIT_DIR/info/grafts";
+ my ($grafts, $comments) = read_grafts($gr_file);
+ my $gr_sha1;
+
+ if (%$grafts) {
+ # temporarily disable our grafts file to make this idempotent
+ chomp($gr_sha1 = safe_qx(qw/git-hash-object -w/,$gr_file));
+ rename $gr_file, "$gr_file~$gr_sha1" or croak $!;
+ }
+
+ my $l_map = read_url_paths();
+ my @re = map { qr/$_/is } @_opt_m if @_opt_m;
+ unless ($_no_default_regex) {
+ push @re, ( qr/\b(?:merge|merging|merged)\s+(\S.+)/is,
+ qr/\b(?:from|of)\s+(\S.+)/is );
+ }
+ foreach my $u (keys %$l_map) {
+ if (@re) {
+ foreach my $p (keys %{$l_map->{$u}}) {
+ graft_merge_msg($grafts,$l_map,$u,$p);
+ }
+ }
+ graft_file_copy($grafts,$l_map,$u) unless $_no_graft_copy;
+ }
+
+ write_grafts($grafts, $comments, $gr_file);
+ unlink "$gr_file~$gr_sha1" if $gr_sha1;
+}
+
+sub multi_init {
+ my $url = shift;
+ $_trunk ||= 'trunk';
+ $_trunk =~ s#/+$##;
+ $url =~ s#/+$## if $url;
+ if ($_trunk !~ m#^[a-z\+]+://#) {
+ $_trunk = '/' . $_trunk if ($_trunk !~ m#^/#);
+ unless ($url) {
+ print STDERR "E: '$_trunk' is not a complete URL ",
+ "and a separate URL is not specified\n";
+ exit 1;
+ }
+ $_trunk = $url . $_trunk;
+ }
+ if ($GIT_SVN eq 'git-svn') {
+ print "GIT_SVN_ID set to 'trunk' for $_trunk\n";
+ $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
+ }
+ init_vars();
+ init($_trunk);
+ complete_url_ls_init($url, $_branches, '--branches/-b', '');
+ complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
+}
+
+sub multi_fetch {
+ # try to do trunk first, since branches/tags
+ # may be descended from it.
+ if (-d "$GIT_DIR/svn/trunk") {
+ print "Fetching trunk\n";
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
+ init_vars();
+ fetch(@_);
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ }
+ rec_fetch('', "$GIT_DIR/svn", @_);
+}
+
########################### utility functions #########################
+sub rec_fetch {
+ my ($pfx, $p, @args) = @_;
+ my @dir;
+ foreach (sort <$p/*>) {
+ if (-r "$_/info/url") {
+ $pfx .= '/' if $pfx && $pfx !~ m!/$!;
+ my $id = $pfx . basename $_;
+ next if $id eq 'trunk';
+ print "Fetching $id\n";
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
+ init_vars();
+ fetch(@args);
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ } elsif (-d $_) {
+ push @dir, $_;
+ }
+ }
+ foreach (@dir) {
+ my $x = $_;
+ $x =~ s!^\Q$GIT_DIR\E/svn/!!;
+ rec_fetch($x, $_);
+ }
+}
+
+sub complete_url_ls_init {
+ my ($url, $var, $switch, $pfx) = @_;
+ unless ($var) {
+ print STDERR "W: $switch not specified\n";
+ return;
+ }
+ $var =~ s#/+$##;
+ if ($var !~ m#^[a-z\+]+://#) {
+ $var = '/' . $var if ($var !~ m#^/#);
+ unless ($url) {
+ print STDERR "E: '$var' is not a complete URL ",
+ "and a separate URL is not specified\n";
+ exit 1;
+ }
+ $var = $url . $var;
+ }
+ chomp(my @ls = safe_qx(qw/svn ls --non-interactive/, $var));
+ my $old = $GIT_SVN;
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ foreach my $u (map { "$var/$_" } (grep m!/$!, @ls)) {
+ $u =~ s#/+$##;
+ if ($u !~ m!\Q$var\E/(.+)$!) {
+ print STDERR "W: Unrecognized URL: $u\n";
+ die "This should never happen\n";
+ }
+ my $id = $pfx.$1;
+ print "init $u => $id\n";
+ $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
+ init_vars();
+ init($u);
+ }
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+}
+
+sub common_prefix {
+ my $paths = shift;
+ my %common;
+ foreach (@$paths) {
+ my @tmp = split m#/#, $_;
+ my $p = '';
+ while (my $x = shift @tmp) {
+ $p .= "/$x";
+ $common{$p} ||= 0;
+ $common{$p}++;
+ }
+ }
+ foreach (sort {length $b <=> length $a} keys %common) {
+ if ($common{$_} == @$paths) {
+ return $_;
+ }
+ }
+ return '';
+}
+
+# this isn't funky-filename safe, but good enough for now...
+sub graft_file_copy {
+ my ($grafts, $l_map, $u) = @_;
+ my $paths = $l_map->{$u};
+ my $pfx = common_prefix([keys %$paths]);
+
+ my $pid = open my $fh, '-|';
+ defined $pid or croak $!;
+ unless ($pid) {
+ exec(qw/svn log -v/, $u.$pfx) or croak $!;
+ }
+ my ($r, $mp) = (undef, undef);
+ while (<$fh>) {
+ chomp;
+ if (/^\-{72}$/) {
+ $mp = $r = undef;
+ } elsif (/^r(\d+) \| /) {
+ $r = $1 unless defined $r;
+ } elsif (/^Changed paths:/) {
+ $mp = 1;
+ } elsif ($mp && m#^ [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) {
+ my $dbg = "r$r | $_";
+ my ($p1, $p0, $r0) = ($1, $2, $3);
+ my $c;
+ foreach my $x (keys %$paths) {
+ next unless ($p1 =~ /^\Q$x\E/);
+ my $i = $paths->{$x};
+ my $f = "$GIT_DIR/svn/$i/revs/$r";
+ unless (-r $f) {
+ print STDERR "r$r of $i not imported,",
+ " $dbg\n";
+ next;
+ }
+ $c = file_to_s($f);
+ }
+ next unless $c;
+ foreach my $x (keys %$paths) {
+ next unless ($p0 =~ /^\Q$x\E/);
+ my $i = $paths->{$x};
+ my $f = "$GIT_DIR/svn/$i/revs/$r0";
+ while ($r0 && !-r $f) {
+ # could be an older revision, too...
+ $r0--;
+ $f = "$GIT_DIR/svn/$i/revs/$r0";
+ }
+ unless (-r $f) {
+ print STDERR "r$r0 of $i not imported,",
+ " $dbg\n";
+ next;
+ }
+ my $r1 = file_to_s($f);
+ $grafts->{$c}->{$r1} = 1;
+ }
+ }
+ }
+}
+
+sub process_merge_msg_matches {
+ my ($grafts, $l_map, $u, $p, $c, @matches) = @_;
+ my (@strong, @weak);
+ foreach (@matches) {
+ # merging with ourselves is not interesting
+ next if $_ eq $p;
+ if ($l_map->{$u}->{$_}) {
+ push @strong, $_;
+ } else {
+ push @weak, $_;
+ }
+ }
+ foreach my $w (@weak) {
+ last if @strong;
+ # no exact match, use branch name as regexp.
+ my $re = qr/\Q$w\E/i;
+ foreach (keys %{$l_map->{$u}}) {
+ if (/$re/) {
+ push @strong, $_;
+ last;
+ }
+ }
+ last if @strong;
+ $w = basename($w);
+ $re = qr/\Q$w\E/i;
+ foreach (keys %{$l_map->{$u}}) {
+ if (/$re/) {
+ push @strong, $_;
+ last;
+ }
+ }
+ }
+ my ($rev) = ($c->{m} =~ /^git-svn-id:\s(?:\S+?)\@(\d+)
+ \s(?:[a-f\d\-]+)$/xsm);
+ unless (defined $rev) {
+ ($rev) = ($c->{m} =~/^git-svn-id:\s(\d+)
+ \@(?:[a-f\d\-]+)/xsm);
+ return unless defined $rev;
+ }
+ foreach my $m (@strong) {
+ my ($r0, $s0) = find_rev_before($rev, $m);
+ $grafts->{$c->{c}}->{$s0} = 1 if defined $s0;
+ }
+}
+
+sub graft_merge_msg {
+ my ($grafts, $l_map, $u, $p, @re) = @_;
+
+ my $x = $l_map->{$u}->{$p};
+ my $rl = rev_list_raw($x);
+ while (my $c = next_rev_list_entry($rl)) {
+ foreach my $re (@re) {
+ my (@br) = ($c->{m} =~ /$re/g);
+ next unless @br;
+ process_merge_msg_matches($grafts,$l_map,$u,$p,$c,@br);
+ }
+ }
+}
+
sub read_uuid {
return if $SVN_UUID;
my $info = shift || svn_info('.');
@@ -402,6 +695,7 @@ sub repo_path_split {
$url .= "/$n";
}
push @repo_path_split_cache, qr/^(\Q$url\E)/;
+ $path = join('/',@paths);
return ($url, $path);
}
@@ -806,6 +1100,38 @@ sub svn_commit_tree {
return fetch("$committed=$commit")->{revision};
}
+sub rev_list_raw {
+ my (@args) = @_;
+ my $pid = open my $fh, '-|';
+ defined $pid or croak $!;
+ if (!$pid) {
+ exec(qw/git-rev-list --pretty=raw/, @args) or croak $!;
+ }
+ return { fh => $fh, t => { } };
+}
+
+sub next_rev_list_entry {
+ my $rl = shift;
+ my $fh = $rl->{fh};
+ my $x = $rl->{t};
+ while (<$fh>) {
+ if (/^commit ($sha1)$/o) {
+ if ($x->{c}) {
+ $rl->{t} = { c => $1 };
+ return $x;
+ } else {
+ $x->{c} = $1;
+ }
+ } elsif (/^parent ($sha1)$/o) {
+ $x->{p}->{$1} = 1;
+ } elsif (s/^ //) {
+ $x->{m} ||= '';
+ $x->{m} .= $_;
+ }
+ }
+ return ($x != $rl->{t}) ? $x : undef;
+}
+
# read the entire log into a temporary file (which is removed ASAP)
# and store the file handle + parser state
sub svn_log_raw {
@@ -1318,6 +1644,16 @@ sub svn_propget_base {
return safe_qx(qw/svn propget/, $p, $f);
}
+sub git_svn_each {
+ my $sub = shift;
+ foreach (`git-rev-parse --symbolic --all`) {
+ next unless s#^refs/remotes/##;
+ chomp $_;
+ next unless -f "$GIT_DIR/svn/$_/info/url";
+ &$sub($_);
+ }
+}
+
sub migration_check {
return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR);
print "Upgrading repository...\n";
@@ -1344,6 +1680,16 @@ sub migration_check {
print "Done upgrading.\n";
}
+sub find_rev_before {
+ my ($r, $git_svn_id) = @_;
+ my @revs = map { basename $_ } <$GIT_DIR/svn/$git_svn_id/revs/*>;
+ foreach my $r0 (sort { $b <=> $a } @revs) {
+ next if $r0 >= $r;
+ return ($r0, file_to_s("$GIT_DIR/svn/$git_svn_id/revs/$r0"));
+ }
+ return (undef, undef);
+}
+
sub init_vars {
$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN";
@@ -1384,6 +1730,79 @@ sub set_default_vals {
}
}
+sub read_grafts {
+ my $gr_file = shift;
+ my ($grafts, $comments) = ({}, {});
+ if (open my $fh, '<', $gr_file) {
+ my @tmp;
+ while (<$fh>) {
+ if (/^($sha1)\s+/) {
+ my $c = $1;
+ if (@tmp) {
+ @{$comments->{$c}} = @tmp;
+ @tmp = ();
+ }
+ foreach my $p (split /\s+/, $_) {
+ $grafts->{$c}->{$p} = 1;
+ }
+ } else {
+ push @tmp, $_;
+ }
+ }
+ close $fh or croak $!;
+ @{$comments->{'END'}} = @tmp if @tmp;
+ }
+ return ($grafts, $comments);
+}
+
+sub write_grafts {
+ my ($grafts, $comments, $gr_file) = @_;
+
+ open my $fh, '>', $gr_file or croak $!;
+ foreach my $c (sort keys %$grafts) {
+ if ($comments->{$c}) {
+ print $fh $_ foreach @{$comments->{$c}};
+ }
+ my $p = $grafts->{$c};
+ delete $p->{$c}; # commits are not self-reproducing...
+ my $pid = open my $ch, '-|';
+ defined $pid or croak $!;
+ if (!$pid) {
+ exec(qw/git-cat-file commit/, $c) or croak $!;
+ }
+ while (<$ch>) {
+ if (/^parent ([a-f\d]{40})/) {
+ $p->{$1} = 1;
+ } else {
+ last unless /^\S/i;
+ }
+ }
+ close $ch; # breaking the pipe
+ print $fh $c, ' ', join(' ', sort keys %$p),"\n";
+ }
+ if ($comments->{'END'}) {
+ print $fh $_ foreach @{$comments->{'END'}};
+ }
+ close $fh or croak $!;
+}
+
+sub read_url_paths {
+ my $l_map = {};
+ git_svn_each(sub { my $x = shift;
+ my $u = file_to_s("$GIT_DIR/svn/$x/info/repo_url");
+ my $p = file_to_s("$GIT_DIR/svn/$x/info/repo_path");
+ # we hate trailing slashes
+ if ($u =~ s#(?:^\/+|\/+$)##g) {
+ s_to_file($u,"$GIT_DIR/svn/$x/info/repo_url");
+ }
+ if ($p =~ s#(?:^\/+|\/+$)##g) {
+ s_to_file($p,"$GIT_DIR/svn/$x/info/repo_path");
+ }
+ $l_map->{$u}->{$p} = $x;
+ });
+ return $l_map;
+}
+
__END__
Data structures:
--
1.4.0
^ permalink raw reply related
* [PATCH 6/13] git-svn: minor cleanups, extra error-checking
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
While we're at it, read_repo_config has been added and
expanded to handle case where command-line arguments are
optional to Getopt::Long
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 82 ++++++++++++++++++++++++------------------
1 files changed, 46 insertions(+), 36 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 2dce4e7..a243060 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -77,39 +77,15 @@ for (my $i = 0; $i < @ARGV; $i++) {
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
-# convert GetOpt::Long specs for use by git-repo-config
-foreach my $o (keys %opts) {
- my $v = $opts{$o};
- my ($key) = ($o =~ /^([a-z\-]+)/);
- $key =~ s/-//g;
- my $arg = 'git-repo-config';
- $arg .= ' --int' if ($o =~ /=i$/);
- $arg .= ' --bool' if ($o !~ /=[sfi]$/);
- if (ref $v eq 'ARRAY') {
- chomp(my @tmp = `$arg --get-all svn.$key`);
- @$v = @tmp if @tmp;
- } else {
- chomp(my $tmp = `$arg --get svn.$key`);
- if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) {
- $$v = $tmp;
- }
- }
-}
-
+read_repo_config(\%opts);
GetOptions(%opts, 'help|H|h' => \$_help,
'version|V' => \$_version,
'id|i=s' => \$GIT_SVN) or exit 1;
-$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
-$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN";
-$GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
-$SVN_URL = undef;
-$REV_DIR = "$GIT_SVN_DIR/revs";
-$SVN_WC = "$GIT_SVN_DIR/tree";
-
usage(0) if $_help;
version() if $_version;
usage(1) unless defined $cmd;
+init_vars();
load_authors() if $_authors;
load_all_refs() if $_branch_all_refs;
svn_compat_check();
@@ -132,7 +108,7 @@ Usage: $0 <command> [options] [arguments
print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n";
foreach (keys %{$cmd{$_}->[2]}) {
# prints out arguments as they should be passed:
- my $x = s#=s$## ? '<arg>' : s#=i$## ? '<num>' : '';
+ my $x = s#[:=]s$## ? '<arg>' : s#[:=]i$## ? '<num>' : '';
print $fd ' ' x 17, join(', ', map { length $_ > 1 ?
"--$_" : "-$_" }
split /\|/,$_)," $x\n";
@@ -220,9 +196,10 @@ sub rebuild {
sys(@svn_up,"-r$newest_rev");
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
index_changes();
- exec('git-write-tree');
+ exec('git-write-tree') or croak $!;
}
waitpid $pid, 0;
+ croak $? if $?;
if ($_upgrade) {
print STDERR <<"";
@@ -295,6 +272,7 @@ sub fetch {
unless (-e "$GIT_DIR/refs/heads/master") {
sys(qw(git-update-ref refs/heads/master),$last_commit);
}
+ close $svn_log->{fh};
return $last;
}
@@ -830,7 +808,7 @@ sub svn_log_raw {
exec (qw(svn log), @log_args) or croak $!
}
waitpid $pid, 0;
- croak if $?;
+ croak $? if $?;
seek $log_fh, 0, 0 or croak $!;
return { state => 'sep', fh => $log_fh };
}
@@ -1090,7 +1068,7 @@ sub git_commit {
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
index_changes();
chomp(my $tree = `git-write-tree`);
- croak if $?;
+ croak $? if $?;
if (exists $tree_map{$tree}) {
my %seen_parent = map { $_ => 1 } @exec_parents;
foreach (@{$tree_map{$tree}}) {
@@ -1118,7 +1096,7 @@ sub git_commit {
exec @exec or croak $!;
}
waitpid($pid,0);
- croak if $?;
+ croak $? if $?;
$out_fh->flush == 0 or croak $!;
seek $out_fh, 0, 0 or croak $!;
@@ -1134,7 +1112,7 @@ sub git_commit {
close STDERR;
close STDOUT;
exec 'git-rev-parse','--verify',
- "refs/remotes/$GIT_SVN^0";
+ "refs/remotes/$GIT_SVN^0" or croak $!;
}
waitpid $pid, 0;
push @update_ref, $primary_parent unless $?;
@@ -1190,7 +1168,7 @@ sub blob_to_file {
if ($pid == 0) {
open STDOUT, '>&', $blob_fh or croak $!;
- exec('git-cat-file','blob',$blob);
+ exec('git-cat-file','blob',$blob) or croak $!;
}
waitpid $pid, 0;
croak $? if $?;
@@ -1202,7 +1180,7 @@ sub safe_qx {
my $pid = open my $child, '-|';
defined $pid or croak $!;
if ($pid == 0) {
- exec(@_) or croak $?;
+ exec(@_) or croak $!;
}
my @ret = (<$child>);
close $child or croak $?;
@@ -1252,7 +1230,7 @@ sub check_upgrade_needed {
defined $pid or croak $!;
if ($pid == 0) {
close STDERR;
- exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $?;
+ exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $!;
}
my @ret = (<$child>);
close $child or croak $?;
@@ -1276,7 +1254,7 @@ sub map_tree_joins {
defined $pid or croak $!;
if ($pid == 0) {
exec(qw(git-rev-list --topo-order --pretty=raw), $br)
- or croak $?;
+ or croak $!;
}
while (<$pipe>) {
if (/^commit ($sha1)$/o) {
@@ -1352,6 +1330,38 @@ sub migration_check {
print "Done upgrading.\n";
}
+sub init_vars {
+ $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
+ $GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN";
+ $GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
+ $SVN_URL = undef;
+ $REV_DIR = "$GIT_SVN_DIR/revs";
+ $SVN_WC = "$GIT_SVN_DIR/tree";
+}
+
+# convert GetOpt::Long specs for use by git-repo-config
+sub read_repo_config {
+ return unless -d $GIT_DIR;
+ my $opts = shift;
+ foreach my $o (keys %$opts) {
+ my $v = $opts->{$o};
+ my ($key) = ($o =~ /^([a-z\-]+)/);
+ $key =~ s/-//g;
+ my $arg = 'git-repo-config';
+ $arg .= ' --int' if ($o =~ /[:=]i$/);
+ $arg .= ' --bool' if ($o !~ /[:=][sfi]$/);
+ if (ref $v eq 'ARRAY') {
+ chomp(my @tmp = `$arg --get-all svn.$key`);
+ @$v = @tmp if @tmp;
+ } else {
+ chomp(my $tmp = `$arg --get svn.$key`);
+ if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) {
+ $$v = $tmp;
+ }
+ }
+ }
+}
+
__END__
Data structures:
--
1.4.0
^ permalink raw reply related
* [PATCH 3/13] git-svn: optimize --branch and --branch-all-ref
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
By breaking the pipe read once we've seen a commit twice.
This should make -B/--branch-all-ref faster and usable on a
frequent basis.
We use topological order now for calling git-rev-list, and any
commit we've seen before should imply that all parents have been
seen (at least I hope that's the case for --topo-order).
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 11 +++++++++--
1 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index c91160d..d4b9323 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -1220,23 +1220,30 @@ sub check_upgrade_needed {
# fills %tree_map with a reverse mapping of trees to commits. Useful
# for finding parents to commit on.
sub map_tree_joins {
+ my %seen;
foreach my $br (@_branch_from) {
my $pid = open my $pipe, '-|';
defined $pid or croak $!;
if ($pid == 0) {
- exec(qw(git-rev-list --pretty=raw), $br) or croak $?;
+ exec(qw(git-rev-list --topo-order --pretty=raw), $br)
+ or croak $?;
}
while (<$pipe>) {
if (/^commit ($sha1)$/o) {
my $commit = $1;
+
+ # if we've seen a commit,
+ # we've seen its parents
+ last if $seen{$commit};
my ($tree) = (<$pipe> =~ /^tree ($sha1)$/o);
unless (defined $tree) {
die "Failed to parse commit $commit\n";
}
push @{$tree_map{$tree}}, $commit;
+ $seen{$commit} = 1;
}
}
- close $pipe or croak $?;
+ close $pipe; # we could be breaking the pipe early
}
}
--
1.4.0
^ permalink raw reply related
* [PATCH 10/13] git-svn: add UTF-8 message test
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/t/t0000-contrib-git-svn.sh | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh
index a07fbad..0c6ff20 100644
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh
@@ -4,6 +4,7 @@ # Copyright (c) 2006 Eric Wong
#
test_description='git-svn tests'
+GIT_SVN_LC_ALL=$LC_ALL
. ./lib-git-svn.sh
mkdir import
@@ -163,6 +164,18 @@ test_expect_success "$name" \
diff -u help $SVN_TREE/exec-2.sh"
+if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$'
+then
+ name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
+ echo '# hello' >> exec-2.sh
+ git update-index exec-2.sh
+ git commit -m 'éïâ'
+ export LC_ALL="$GIT_SVN_LC_ALL"
+ test_expect_success "$name" "git-svn commit HEAD"
+ unset LC_ALL
+else
+ echo "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)"
+fi
name='test fetch functionality (svn => git) with alternate GIT_SVN_ID'
GIT_SVN_ID=alt
--
1.4.0
^ permalink raw reply related
* [PATCH 13/13] git-svn: make the $GIT_DIR/svn/*/revs directory obsolete
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
This is a very intrusive change, so I've beefed up the tests
significantly. Added 'full-test' a target to the Makefile,
to test different possible configurations. This is intended
for maintainers only. Users should only be concerned with
'test' succeeding.
We now have a very simple custom database format for handling
mapping of svn revisions => git commits. Of course, we're
not really using it yet, either.
Also disabled automatic branch-finding on new trees for now.
It's too easily broken. revisions_eq() function should be
helpful for branch detection.
Also removed an extra assertion in fetch_cmd() that wasn't
correctly done. This bug was found by full-test.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/Makefile | 12 +
contrib/git-svn/git-svn.perl | 245 ++++++++++++++--------
contrib/git-svn/t/t0000-contrib-git-svn.sh | 13 +
contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 86 ++++----
4 files changed, 224 insertions(+), 132 deletions(-)
diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile
index 48f60b3..d73aa56 100644
--- a/contrib/git-svn/Makefile
+++ b/contrib/git-svn/Makefile
@@ -29,8 +29,16 @@ git-svn.html : git-svn.txt
asciidoc -b xhtml11 -d manpage \
-f ../../Documentation/asciidoc.conf $<
test: git-svn
- cd t && $(SHELL) ./t0000-contrib-git-svn.sh
- cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh
+ cd t && $(SHELL) ./t0000-contrib-git-svn.sh $(TEST_FLAGS)
+ cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh $(TEST_FLAGS)
+
+full-test:
+ $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1
+ $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1
+ $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+ LC_ALL=en_US.UTF-8
+ $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+ LC_ALL=en_US.UTF-8
clean:
rm -f git-svn *.xml *.html *.1
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 9618c8b..884969e 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -6,9 +6,9 @@ use strict;
use vars qw/ $AUTHOR $VERSION
$SVN_URL $SVN_INFO $SVN_WC $SVN_UUID
$GIT_SVN_INDEX $GIT_SVN
- $GIT_DIR $REV_DIR $GIT_SVN_DIR/;
+ $GIT_DIR $GIT_SVN_DIR $REVDB/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '1.1.0-pre';
+$VERSION = '1.1.1-broken';
use Cwd qw/abs_path/;
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
@@ -31,10 +31,13 @@ use File::Path qw/mkpath/;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
use File::Spec qw//;
use POSIX qw/strftime/;
+use Memoize;
+memoize('revisions_eq');
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
libsvn_load();
+my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
@@ -43,7 +46,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m);
-my (@_branch_from, %tree_map, %users, %rusers);
+my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@@ -201,7 +204,6 @@ sub rebuild {
next if (defined $SVN_UUID && ($uuid ne $SVN_UUID));
next if (defined $SVN_URL && defined $url && ($url ne $SVN_URL));
- print "r$rev = $c\n";
unless (defined $latest) {
if (!$SVN_URL && !$url) {
croak "SVN repository location required: $url\n";
@@ -211,8 +213,8 @@ sub rebuild {
setup_git_svn();
$latest = $rev;
}
- assert_revision_eq_or_unknown($rev, $c);
- sys('git-update-ref',"svn/$GIT_SVN/revs/$rev",$c);
+ revdb_set($REVDB, $rev, $c);
+ print "r$rev = $c\n";
$newest_rev = $rev if ($rev > $newest_rev);
}
close $rev_list or croak $?;
@@ -280,7 +282,11 @@ sub fetch_cmd {
my $svn_log = svn_log_raw(@log_args);
my $base = next_log_entry($svn_log) or croak "No base revision!\n";
- my $last_commit = undef;
+ # don't need last_revision from grab_base_rev() because
+ # user could've specified a different revision to skip (they
+ # didn't want to import certain revisions into git for whatever
+ # reason, so trust $base->{revision} instead.
+ my (undef, $last_commit) = svn_grab_base_rev();
unless (-d $SVN_WC) {
svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC);
chdir $SVN_WC or croak $!;
@@ -290,7 +296,6 @@ sub fetch_cmd {
} else {
chdir $SVN_WC or croak $!;
read_uuid();
- eval { $last_commit = file_to_s("$REV_DIR/$base->{revision}") };
# looks like a user manually cp'd and svn switch'ed
unless ($last_commit) {
sys(qw/svn revert -R ./);
@@ -303,7 +308,6 @@ sub fetch_cmd {
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
my $last = $base;
while (my $log_msg = next_log_entry($svn_log)) {
- assert_tree($last_commit);
if ($last->{revision} >= $log_msg->{revision}) {
croak "Out of order: last >= current: ",
"$last->{revision} >= $log_msg->{revision}\n";
@@ -444,14 +448,14 @@ sub commit_cmd {
}
$info = svn_info('.');
read_uuid($info);
- my $svn_current_rev = $info->{'Last Changed Rev'};
+ my $last = $fetched;
foreach my $c (@revs) {
- my $mods = svn_checkout_tree($svn_current_rev, $c);
+ my $mods = svn_checkout_tree($last, $c);
if (scalar @$mods == 0) {
print "Skipping, no changes detected\n";
next;
}
- $svn_current_rev = svn_commit_tree($svn_current_rev, $c);
+ $last = svn_commit_tree($last, $c);
}
}
@@ -500,7 +504,7 @@ sub commit_lib {
},
@lock)
);
- my $mods = libsvn_checkout_tree($r_last, $c, $ed);
+ my $mods = libsvn_checkout_tree($cmt_last, $c, $ed);
if (@$mods == 0) {
print "No changes\nr$r_last = $cmt_last\n";
$ed->abort_edit;
@@ -814,7 +818,7 @@ sub graft_file_copy_cmd {
my ($grafts, $l_map, $u) = @_;
my $paths = $l_map->{$u};
my $pfx = common_prefix([keys %$paths]);
-
+ $SVN_URL ||= $u.$pfx;
my $pid = open my $fh, '-|';
defined $pid or croak $!;
unless ($pid) {
@@ -851,6 +855,8 @@ sub graft_file_copy_lib {
my ($base, $head) = libsvn_parse_revision();
my $inc = 1000;
my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc);
+ my $eh = $SVN::Error::handler;
+ $SVN::Error::handler = \&libsvn_skip_unknown_revs;
while (1) {
my $pool = SVN::Pool->new;
$SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1,
@@ -864,6 +870,7 @@ sub graft_file_copy_lib {
$max += $inc;
$max = $head if ($max > $head);
}
+ $SVN::Error::handler = $eh;
}
sub process_merge_msg_matches {
@@ -994,7 +1001,8 @@ sub setup_git_svn {
}
mkpath([$GIT_SVN_DIR]);
mkpath(["$GIT_SVN_DIR/info"]);
- mkpath([$REV_DIR]);
+ open my $fh, '>>',$REVDB or croak $!;
+ close $fh;
s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url");
open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!;
@@ -1201,8 +1209,7 @@ sub precommit_check {
sub get_diff {
- my ($svn_rev, $treeish) = @_;
- my $from = file_to_s("$REV_DIR/$svn_rev");
+ my ($from, $treeish) = @_;
assert_tree($from);
print "diff-tree $from $treeish\n";
my $pid = open my $diff_fh, '-|';
@@ -1222,8 +1229,8 @@ sub get_diff {
}
sub svn_checkout_tree {
- my ($svn_rev, $treeish) = @_;
- my $mods = get_diff($svn_rev, $treeish);
+ my ($from, $treeish) = @_;
+ my $mods = get_diff($from->{commit}, $treeish);
return $mods unless (scalar @$mods);
my ($rm, $add) = precommit_check($mods);
@@ -1268,8 +1275,8 @@ sub svn_checkout_tree {
}
sub libsvn_checkout_tree {
- my ($svn_rev, $treeish, $ed) = @_;
- my $mods = get_diff($svn_rev, $treeish);
+ my ($from, $treeish, $ed) = @_;
+ my $mods = get_diff($from, $treeish);
return $mods unless (scalar @$mods);
my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
@@ -1365,7 +1372,7 @@ sub get_commit_message {
}
sub svn_commit_tree {
- my ($svn_rev, $commit) = @_;
+ my ($last, $commit) = @_;
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
my $log_msg = get_commit_message($commit, $commit_msg);
my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/);
@@ -1392,7 +1399,7 @@ sub svn_commit_tree {
my @svn_up = qw(svn up);
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
- if ($committed == ($svn_rev + 1)) {
+ if ($_optimize_commits && ($committed == ($last->{revision} + 1))) {
push @svn_up, "-r$committed";
sys(@svn_up);
my $info = svn_info('.');
@@ -1408,14 +1415,14 @@ sub svn_commit_tree {
$log_msg->{author} = $info->{'Last Changed Author'};
$log_msg->{revision} = $committed;
$log_msg->{msg} .= "\n";
- my $parent = file_to_s("$REV_DIR/$svn_rev");
- git_commit($log_msg, $parent, $commit);
- return $committed;
+ $log_msg->{parents} = [ $last->{commit} ];
+ $log_msg->{commit} = git_commit($log_msg, $commit);
+ return $log_msg;
}
# resync immediately
- push @svn_up, "-r$svn_rev";
+ push @svn_up, "-r$last->{revision}";
sys(@svn_up);
- return fetch("$committed=$commit")->{revision};
+ return fetch("$committed=$commit");
}
sub rev_list_raw {
@@ -1671,10 +1678,9 @@ sub file_to_s {
}
sub assert_revision_unknown {
- my $revno = shift;
- if (-f "$REV_DIR/$revno") {
- croak "$REV_DIR/$revno already exists! ",
- "Why are we refetching it?";
+ my $r = shift;
+ if (my $c = revdb_get($REVDB, $r)) {
+ croak "$r = $c already exists! Why are we refetching it?";
}
}
@@ -1690,18 +1696,6 @@ sub trees_eq {
return 1;
}
-sub assert_revision_eq_or_unknown {
- my ($revno, $commit) = @_;
- if (-f "$REV_DIR/$revno") {
- my $current = file_to_s("$REV_DIR/$revno");
- if (($commit ne $current) && !trees_eq($commit, $current)) {
- croak "$REV_DIR/$revno already exists!\n",
- "current: $current\nexpected: $commit\n";
- }
- return;
- }
-}
-
sub git_commit {
my ($log_msg, @parents) = @_;
assert_revision_unknown($log_msg->{revision});
@@ -1763,19 +1757,12 @@ sub git_commit {
}
my @update_ref = ('git-update-ref',"refs/remotes/$GIT_SVN",$commit);
if (my $primary_parent = shift @exec_parents) {
- $pid = fork;
- defined $pid or croak $!;
- if (!$pid) {
- close STDERR;
- close STDOUT;
- exec 'git-rev-parse','--verify',
- "refs/remotes/$GIT_SVN^0" or croak $!;
- }
- waitpid $pid, 0;
+ quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0");
push @update_ref, $primary_parent unless $?;
}
sys(@update_ref);
- sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit);
+ revdb_set($REVDB, $log_msg->{revision}, $commit);
+
# this output is read via pipe, do not change:
print "r$log_msg->{revision} = $commit\n";
if ($_repack && (--$_repack_nr == 0)) {
@@ -1990,7 +1977,29 @@ sub git_svn_each {
}
}
+sub migrate_revdb {
+ git_svn_each(sub {
+ my $id = shift;
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
+ init_vars();
+ exit 0 if -r $REVDB;
+ print "Upgrading svn => git mapping...\n";
+ open my $fh, '>>',$REVDB or croak $!;
+ close $fh;
+ rebuild();
+ print "Done upgrading. You may now delete the ",
+ "deprecated $GIT_SVN_DIR/revs directory\n";
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ });
+}
+
sub migration_check {
+ migrate_revdb() unless (-e $REVDB);
return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR);
print "Upgrading repository...\n";
unless (-d "$GIT_DIR/svn") {
@@ -2013,15 +2022,19 @@ sub migration_check {
s_to_file($url, "$GIT_DIR/svn/$x/info/repo_url");
s_to_file($path, "$GIT_DIR/svn/$x/info/repo_path");
}
+ migrate_revdb() if (-d $GIT_SVN_DIR && !-w $REVDB);
print "Done upgrading.\n";
}
sub find_rev_before {
- my ($r, $git_svn_id) = @_;
- my @revs = map { basename $_ } <$GIT_DIR/svn/$git_svn_id/revs/*>;
- foreach my $r0 (sort { $b <=> $a } @revs) {
- next if $r0 >= $r;
- return ($r0, file_to_s("$GIT_DIR/svn/$git_svn_id/revs/$r0"));
+ my ($r, $id, $eq_ok) = @_;
+ my $f = "$GIT_DIR/svn/$id/.rev_db";
+ # --$r unless $eq_ok;
+ while ($r > 0) {
+ if (my $c = revdb_get($f, $r)) {
+ return ($r, $c);
+ }
+ --$r;
}
return (undef, undef);
}
@@ -2029,9 +2042,9 @@ sub find_rev_before {
sub init_vars {
$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN";
+ $REVDB = "$GIT_SVN_DIR/.rev_db";
$GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
$SVN_URL = undef;
- $REV_DIR = "$GIT_SVN_DIR/revs";
$SVN_WC = "$GIT_SVN_DIR/tree";
}
@@ -2491,7 +2504,27 @@ sub libsvn_traverse_ignore {
$pool->clear;
}
-sub libsvn_new_tree {
+sub revisions_eq {
+ my ($path, $r0, $r1) = @_;
+ return 1 if $r0 == $r1;
+ my $nr = 0;
+ if ($_use_lib) {
+ # should be OK to use Pool here (r1 - r0) should be small
+ my $pool = SVN::Pool->new;
+ $SVN->get_log("/$path", $r0, $r1, 0, 1, 1, sub {$nr++},$pool);
+ $pool->clear;
+ } else {
+ my ($url, undef) = repo_path_split($SVN_URL);
+ my $svn_log = svn_log_raw("$url/$path","-r$r0:$r1");
+ while (next_log_entry($svn_log)) { $nr++ }
+ close $svn_log->{fh};
+ }
+ return 0 if ($nr > 1);
+ return 1;
+}
+
+sub libsvn_find_parent_branch {
+ return undef; # XXX this function is disabled atm (not tested enough)
my ($paths, $rev, $author, $date, $msg) = @_;
my $svn_path = '/'.$SVN_PATH;
@@ -2502,27 +2535,33 @@ sub libsvn_new_tree {
my $branch_from = $i->copyfrom_path or next;
my $r = $i->copyfrom_rev;
print STDERR "Found possible branch point: ",
- "$branch_from => $svn_path, $r\n";
+ "$branch_from => $svn_path, $r\n";
$branch_from =~ s#^/##;
my $l_map = read_url_paths();
my $url = $SVN->{url};
defined $l_map->{$url} or next;
my $id = $l_map->{$url}->{$branch_from} or next;
- my $f = "$GIT_DIR/svn/$id/revs/$r";
- while ($r && !-r $f) {
- $r--;
- $f = "$GIT_DIR/svn/$id/revs/$r";
- }
- if (-r $f) {
- my $parent = file_to_s($f);
+ my ($r0, $parent) = find_rev_before($r,$id,1);
+ if (defined $r0 && defined $parent &&
+ revisions_eq($branch_from, $r0, $r)) {
unlink $GIT_SVN_INDEX;
print STDERR "Found branch parent: $parent\n";
sys(qw/git-read-tree/, $parent);
return libsvn_fetch($parent, $paths, $rev,
$author, $date, $msg);
+ } else {
+ print STDERR
+ "Nope, branch point not imported or unknown\n";
}
- print STDERR "Nope, branch point not imported or unknown\n";
}
+ return undef;
+}
+
+sub libsvn_new_tree {
+ if (my $log_entry = libsvn_find_parent_branch(@_)) {
+ return $log_entry;
+ }
+ my ($paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
my $pool = SVN::Pool->new;
libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool);
@@ -2536,10 +2575,8 @@ sub find_graft_path_commit {
foreach my $x (keys %$tree_paths) {
next unless ($p1 =~ /^\Q$x\E/);
my $i = $tree_paths->{$x};
- my $f = "$GIT_DIR/svn/$i/revs/$r1";
-
- return file_to_s($f) if (-r $f);
-
+ my ($r0, $parent) = find_rev_before($r1,$i,1);
+ return $parent if (defined $r0 && $r0 == $r1);
print STDERR "r$r1 of $i not imported\n";
next;
}
@@ -2551,18 +2588,10 @@ sub find_graft_path_parents {
foreach my $x (keys %$tree_paths) {
next unless ($p0 =~ /^\Q$x\E/);
my $i = $tree_paths->{$x};
- my $f = "$GIT_DIR/svn/$i/revs/$r0";
- while ($r0 && !-r $f) {
- # could be an older revision, too...
- $r0--;
- $f = "$GIT_DIR/svn/$i/revs/$r0";
- }
- unless (-r $f) {
- print STDERR "r$r0 of $i not imported\n";
- next;
+ my ($r, $parent) = find_rev_before($r0, $i, 1);
+ if (defined $r && defined $parent && revisions_eq($x,$r,$r0)) {
+ $grafts->{$c}->{$parent} = 1;
}
- my $parent = file_to_s($f);
- $grafts->{$c}->{$parent} = 1;
}
}
@@ -2600,8 +2629,7 @@ sub restore_index {
sub libsvn_commit_cb {
my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_;
- if ($rev == ($r_last + 1)) {
- # optimized (avoid fetch)
+ if ($_optimize_commits && $rev == ($r_last + 1)) {
my $log = libsvn_log_entry($rev,$committer,$date,$msg);
$log->{tree} = get_tree_from_treeish($c);
my $cmt = git_commit($log, $cmt_last, $c);
@@ -2652,6 +2680,49 @@ sub libsvn_skip_unknown_revs {
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
};
+# Tie::File seems to be prone to offset errors if revisions get sparse,
+# it's not that fast, either. Tie::File is also not in Perl 5.6. So
+# one of my favorite modules is out :< Next up would be one of the DBM
+# modules, but I'm not sure which is most portable... So I'll just
+# go with something that's plain-text, but still capable of
+# being randomly accessed. So here's my ultra-simple fixed-width
+# database. All records are 40 characters + "\n", so it's easy to seek
+# to a revision: (41 * rev) is the byte offset.
+# A record of 40 0s denotes an empty revision.
+# And yes, it's still pretty fast (faster than Tie::File).
+sub revdb_set {
+ my ($file, $rev, $commit) = @_;
+ length $commit == 40 or croak "arg3 must be a full SHA1 hexsum\n";
+ open my $fh, '+<', $file or croak $!;
+ my $offset = $rev * 41;
+ # assume that append is the common case:
+ seek $fh, 0, 2 or croak $!;
+ my $pos = tell $fh;
+ if ($pos < $offset) {
+ print $fh (('0' x 40),"\n") x (($offset - $pos) / 41);
+ }
+ seek $fh, $offset, 0 or croak $!;
+ print $fh $commit,"\n";
+ close $fh or croak $!;
+}
+
+sub revdb_get {
+ my ($file, $rev) = @_;
+ my $ret;
+ my $offset = $rev * 41;
+ open my $fh, '<', $file or croak $!;
+ seek $fh, $offset, 0;
+ if (tell $fh == $offset) {
+ $ret = readline $fh;
+ if (defined $ret) {
+ chomp $ret;
+ $ret = undef if ($ret =~ /^0{40}$/);
+ }
+ }
+ close $fh or croak $!;
+ return $ret;
+}
+
package SVN::Git::Editor;
use vars qw/@ISA/;
use strict;
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh
index c33b522..f896e2c 100644
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh
@@ -193,5 +193,18 @@ test_expect_success "$name" \
git-rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
diff -u a b"
+name='check imported tree checksums expected tree checksums'
+cat > expected <<\EOF
+tree f735671b89a7eb30cab1d8597de35bd4271ab813
+tree 4b9af72bb861eaed053854ec502cf7df72618f0f
+tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
+tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
+tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
+tree 56a30b966619b863674f5978696f4a3594f2fca9
+tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
+tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
+EOF
+test_expect_success "$name" "diff -u a expected"
+
test_done
diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
index 23a5a2a..54e0ed7 100644
--- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
+++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
@@ -52,49 +52,49 @@ EOF
cd ..
rm -rf import
-svn co "$svnrepo" test_wc
+test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc"
+test_expect_success 'setup some commits to svn' \
+ 'cd test_wc &&
+ echo Greetings >> kw.c &&
+ svn commit -m "Not yet an Id" &&
+ svn up &&
+ echo Hello world >> kw.c &&
+ svn commit -m "Modified file, but still not yet an Id" &&
+ svn up &&
+ svn propset svn:keywords Id kw.c &&
+ svn commit -m "Propset Id" &&
+ svn up &&
+ cd ..'
+
+test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
+test_expect_success 'fetch revisions from svn' 'git-svn fetch'
-cd test_wc
- echo 'Greetings' >> kw.c
- svn commit -m 'Not yet an $Id$'
- svn up
-
- echo 'Hello world' >> kw.c
- svn commit -m 'Modified file, but still not yet an $Id$'
- svn up
-
- svn propset svn:keywords Id kw.c
- svn commit -m 'Propset $Id$'
- svn up
-cd ..
-
-git-svn init "$svnrepo"
-git-svn fetch
-
-git checkout -b mybranch remotes/git-svn
-echo 'Hi again' >> kw.c
name='test svn:keywords ignoring'
-
-git commit -a -m "$name"
-git-svn commit remotes/git-svn..mybranch
-git pull . remotes/git-svn
+test_expect_success "$name" \
+ 'git checkout -b mybranch remotes/git-svn &&
+ echo Hi again >> kw.c &&
+ git commit -a -m "test keywoards ignoring" &&
+ git-svn commit remotes/git-svn..mybranch &&
+ git pull . remotes/git-svn'
expect='/* $Id$ */'
got="`sed -ne 2p kw.c`"
test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
-cd test_wc
- svn propset svn:eol-style CR empty
- svn propset svn:eol-style CR crlf
- svn propset svn:eol-style CR ne_crlf
- svn commit -m 'propset CR on crlf files'
- svn up
-cd ..
+test_expect_success "propset CR on crlf files" \
+ 'cd test_wc &&
+ svn propset svn:eol-style CR empty &&
+ svn propset svn:eol-style CR crlf &&
+ svn propset svn:eol-style CR ne_crlf &&
+ svn commit -m "propset CR on crlf files" &&
+ svn up &&
+ cd ..'
-git-svn fetch
-git pull . remotes/git-svn
+test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
+ "git-svn fetch &&
+ git pull . remotes/git-svn &&
+ svn co $svnrepo new_wc"
-svn co "$svnrepo" new_wc
for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
do
test_expect_success "Comparing $i" "cmp $i new_wc/$i"
@@ -106,16 +106,16 @@ cd test_wc
printf '$Id$\rHello\rWorld' > ne_cr
a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin`
a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin`
- svn propset svn:eol-style CRLF cr
- svn propset svn:eol-style CRLF ne_cr
- svn propset svn:keywords Id cr
- svn propset svn:keywords Id ne_cr
- svn commit -m 'propset CRLF on cr files'
- svn up
+ test_expect_success 'Set CRLF on cr files' \
+ 'svn propset svn:eol-style CRLF cr &&
+ svn propset svn:eol-style CRLF ne_cr &&
+ svn propset svn:keywords Id cr &&
+ svn propset svn:keywords Id ne_cr &&
+ svn commit -m "propset CRLF on cr files" &&
+ svn up'
cd ..
-
-git-svn fetch
-git pull . remotes/git-svn
+test_expect_success 'fetch and pull latest from svn' \
+ 'git-svn fetch && git pull . remotes/git-svn'
b_cr="`git-hash-object cr`"
b_ne_cr="`git-hash-object ne_cr`"
--
1.4.0
^ permalink raw reply related
* [PATCH 11/13] git-svn: add 'log' command, a facsimile of basic `svn log'
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
This quick feature should make it easy to look up svn log
messages when svn users refer to -r/--revision numbers.
The following features from `svn log' are supported:
--revision=<n>[:<n>] - is supported, non-numeric args are not:
HEAD, NEXT, BASE, PREV, etc ...
-v/--verbose - just maps to --raw (in git log), so
it's completely incompatible with
the --verbose output in svn log
--limit=<n> - is NOT the same as --max-count,
doesn't count merged/excluded commits
--incremental - supported (trivial :P)
New features:
--show-commit - shows the git commit sha1, as well
--oneline - our version of --pretty=oneline
Any other arguments are passed directly to `git log'
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 260 +++++++++++++++++++++++++++++++++++++++---
1 files changed, 243 insertions(+), 17 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index d5c7e47..03416ae 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -15,6 +15,7 @@ use Cwd qw/abs_path/;
$ENV{GIT_DIR} = $GIT_DIR;
my $LC_ALL = $ENV{LC_ALL};
+my $TZ = $ENV{TZ};
# make sure the svn binary gives consistent output between locales and TZs:
$ENV{TZ} = 'UTC';
$ENV{LC_ALL} = 'C';
@@ -27,7 +28,7 @@ use Carp qw/croak/;
use IO::File qw//;
use File::Basename qw/dirname basename/;
use File::Path qw/mkpath/;
-use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
+use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
use File::Spec qw//;
use POSIX qw/strftime/;
my $sha1 = qr/[a-f\d]{40}/;
@@ -36,8 +37,9 @@ my ($_revision,$_stdin,$_no_ignore_ext,$
$_find_copies_harder, $_l, $_cp_similarity,
$_repack, $_repack_nr, $_repack_flags,
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
+ $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m);
-my (@_branch_from, %tree_map, %users);
+my (@_branch_from, %tree_map, %users, %rusers);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@@ -87,6 +89,15 @@ my %cmd = (
'multi-fetch' => [ \&multi_fetch,
'Fetch multiple trees (like git-svnimport)',
\%fc_opts ],
+ 'log' => [ \&show_log, 'Show commit logs',
+ { 'limit=i' => \$_limit,
+ 'revision|r=s' => \$_revision,
+ 'verbose|v' => \$_verbose,
+ 'incremental' => \$_incremental,
+ 'oneline' => \$_oneline,
+ 'show-commit' => \$_show_commit,
+ 'authors-file|A=s' => \$_authors,
+ } ],
);
my $cmd;
@@ -101,9 +112,10 @@ for (my $i = 0; $i < @ARGV; $i++) {
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
read_repo_config(\%opts);
-GetOptions(%opts, 'help|H|h' => \$_help,
- 'version|V' => \$_version,
- 'id|i=s' => \$GIT_SVN) or exit 1;
+my $rv = GetOptions(%opts, 'help|H|h' => \$_help,
+ 'version|V' => \$_version,
+ 'id|i=s' => \$GIT_SVN);
+exit 1 if (!$rv && $cmd ne 'log');
set_default_vals();
usage(0) if $_help;
@@ -173,18 +185,10 @@ sub rebuild {
croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o;
my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`);
next if (!@commit); # skip merges
- my $id = $commit[$#commit];
- my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+)
- \s([a-f\d\-]+)$/x);
- if (!$rev || !$uuid || !$url) {
- # some of the original repositories I made had
- # indentifiers like this:
- ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)
- \@([a-f\d\-]+)/x);
- if (!$rev || !$uuid) {
- croak "Unable to extract revision or UUID from ",
- "$c, $id\n";
- }
+ my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]);
+ if (!$rev || !$uuid) {
+ croak "Unable to extract revision or UUID from ",
+ "$c, $commit[$#commit]\n";
}
# if we merged or otherwise started elsewhere, this is
@@ -448,6 +452,81 @@ sub multi_fetch {
rec_fetch('', "$GIT_DIR/svn", @_);
}
+sub show_log {
+ my (@args) = @_;
+ my ($r_min, $r_max);
+ my $r_last = -1; # prevent dupes
+ rload_authors() if $_authors;
+ if (defined $TZ) {
+ $ENV{TZ} = $TZ;
+ } else {
+ delete $ENV{TZ};
+ }
+ if (defined $_revision) {
+ if ($_revision =~ /^(\d+):(\d+)$/) {
+ ($r_min, $r_max) = ($1, $2);
+ } elsif ($_revision =~ /^\d+$/) {
+ $r_min = $r_max = $_revision;
+ } else {
+ print STDERR "-r$_revision is not supported, use ",
+ "standard \'git log\' arguments instead\n";
+ exit 1;
+ }
+ }
+
+ my $pid = open(my $log,'-|');
+ defined $pid or croak $!;
+ if (!$pid) {
+ my @rl = (qw/git-log --abbrev-commit --pretty=raw
+ --default/, "remotes/$GIT_SVN");
+ push @rl, '--raw' if $_verbose;
+ exec(@rl, @args) or croak $!;
+ }
+ setup_pager();
+ my (@k, $c, $d);
+ while (<$log>) {
+ if (/^commit ($sha1_short)/o) {
+ my $cmt = $1;
+ if ($c && defined $c->{r} && $c->{r} != $r_last) {
+ $r_last = $c->{r};
+ process_commit($c, $r_min, $r_max, \@k) or
+ goto out;
+ }
+ $d = undef;
+ $c = { c => $cmt };
+ } elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) {
+ get_author_info($c, $1, $2, $3);
+ } elsif (/^(?:tree|parent|committer) /) {
+ # ignore
+ } elsif (/^:\d{6} \d{6} $sha1_short/o) {
+ push @{$c->{raw}}, $_;
+ } elsif (/^diff /) {
+ $d = 1;
+ push @{$c->{diff}}, $_;
+ } elsif ($d) {
+ push @{$c->{diff}}, $_;
+ } elsif (/^ (git-svn-id:.+)$/) {
+ my ($url, $rev, $uuid) = extract_metadata($1);
+ $c->{r} = $rev;
+ } elsif (s/^ //) {
+ push @{$c->{l}}, $_;
+ }
+ }
+ if ($c && defined $c->{r} && $c->{r} != $r_last) {
+ $r_last = $c->{r};
+ process_commit($c, $r_min, $r_max, \@k);
+ }
+ if (@k) {
+ my $swap = $r_max;
+ $r_max = $r_min;
+ $r_min = $swap;
+ process_commit($_, $r_min, $r_max) foreach reverse @k;
+ }
+out:
+ close $log;
+ print '-' x72,"\n" unless $_incremental || $_oneline;
+}
+
########################### utility functions #########################
sub rec_fetch {
@@ -1638,6 +1717,17 @@ sub load_authors {
close $authors or croak $!;
}
+sub rload_authors {
+ open my $authors, '<', $_authors or die "Can't open $_authors $!\n";
+ while (<$authors>) {
+ chomp;
+ next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/;
+ my ($user, $name, $email) = ($1, $2, $3);
+ $rusers{"$name <$email>"} = $user;
+ }
+ close $authors or croak $!;
+}
+
sub svn_propget_base {
my ($p, $f) = @_;
$f .= '@BASE' if $_svn_pg_peg_revs;
@@ -1803,6 +1893,142 @@ sub read_url_paths {
return $l_map;
}
+sub extract_metadata {
+ my $id = shift;
+ my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+)
+ \s([a-f\d\-]+)$/x);
+ if (!$rev || !$uuid || !$url) {
+ # some of the original repositories I made had
+ # indentifiers like this:
+ ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
+ }
+ return ($url, $rev, $uuid);
+}
+
+sub tz_to_s_offset {
+ my ($tz) = @_;
+ $tz =~ s/(\d\d)$//;
+ return ($1 * 60) + ($tz * 3600);
+}
+
+sub setup_pager { # translated to Perl from pager.c
+ return unless (-t *STDOUT);
+ my $pager = $ENV{PAGER};
+ if (!defined $pager) {
+ $pager = 'less';
+ } elsif (length $pager == 0 || $pager eq 'cat') {
+ return;
+ }
+ pipe my $rfd, my $wfd or return;
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ open STDOUT, '>&', $wfd or croak $!;
+ return;
+ }
+ open STDIN, '<&', $rfd or croak $!;
+ $ENV{LESS} ||= '-S';
+ exec $pager or croak "Can't run pager: $!\n";;
+}
+
+sub get_author_info {
+ my ($dest, $author, $t, $tz) = @_;
+ $author =~ s/(?:^\s*|\s*$)//g;
+ my $_a;
+ if ($_authors) {
+ $_a = $rusers{$author} || undef;
+ }
+ if (!$_a) {
+ ($_a) = ($author =~ /<([^>]+)\@[^>]+>$/);
+ }
+ $dest->{t} = $t;
+ $dest->{tz} = $tz;
+ $dest->{a} = $_a;
+ # Date::Parse isn't in the standard Perl distro :(
+ if ($tz =~ s/^\+//) {
+ $t += tz_to_s_offset($tz);
+ } elsif ($tz =~ s/^\-//) {
+ $t -= tz_to_s_offset($tz);
+ }
+ $dest->{t_utc} = $t;
+}
+
+sub process_commit {
+ my ($c, $r_min, $r_max, $defer) = @_;
+ if (defined $r_min && defined $r_max) {
+ if ($r_min == $c->{r} && $r_min == $r_max) {
+ show_commit($c);
+ return 0;
+ }
+ return 1 if $r_min == $r_max;
+ if ($r_min < $r_max) {
+ # we need to reverse the print order
+ return 0 if (defined $_limit && --$_limit < 0);
+ push @$defer, $c;
+ return 1;
+ }
+ if ($r_min != $r_max) {
+ return 1 if ($r_min < $c->{r});
+ return 1 if ($r_max > $c->{r});
+ }
+ }
+ return 0 if (defined $_limit && --$_limit < 0);
+ show_commit($c);
+ return 1;
+}
+
+sub show_commit {
+ my $c = shift;
+ if ($_oneline) {
+ my $x = "\n";
+ if (my $l = $c->{l}) {
+ while ($l->[0] =~ /^\s*$/) { shift @$l }
+ $x = $l->[0];
+ }
+ $_l_fmt ||= 'A' . length($c->{r});
+ print 'r',pack($_l_fmt, $c->{r}),' | ';
+ print "$c->{c} | " if $_show_commit;
+ print $x;
+ } else {
+ show_commit_normal($c);
+ }
+}
+
+sub show_commit_normal {
+ my ($c) = @_;
+ print '-' x72, "\nr$c->{r} | ";
+ print "$c->{c} | " if $_show_commit;
+ print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)",
+ localtime($c->{t_utc})), ' | ';
+ my $nr_line = 0;
+
+ if (my $l = $c->{l}) {
+ while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") {
+ pop @$l;
+ }
+ $nr_line = scalar @$l;
+ if (!$nr_line) {
+ print "1 line\n\n\n";
+ } else {
+ if ($nr_line == 1) {
+ $nr_line = '1 line';
+ } else {
+ $nr_line .= ' lines';
+ }
+ print $nr_line, "\n\n";
+ print $_ foreach @$l;
+ }
+ } else {
+ print "1 line\n\n";
+
+ }
+ foreach my $x (qw/raw diff/) {
+ if ($c->{$x}) {
+ print "\n";
+ print $_ foreach @{$c->{$x}}
+ }
+ }
+}
+
__END__
Data structures:
--
1.4.0
^ permalink raw reply related
* [PATCH 12/13] git-svn: add support for Perl SVN::* libraries
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
This means we no longer have to deal with having bloated SVN
working copies around and we get a nice performance increase as
well because we don't have to exec the SVN binary and start a
new server connection each time.
Of course we have to manually manage memory with SVN::Pool
whenever we can, and hack around cases where SVN just eats
memory despite pools (I blame Perl, too). I would like to
keep memory usage as stable as possible during long fetch/commit
processes since I still use computers with only 256-512M RAM.
commit should always be faster with the SVN library code. The
SVN::Delta interface is leaky (or I'm not using it with pools
correctly), so I'm forking on every commit, but that doesn't
seem to hurt performance too much (at least on normal Unix/Linux
systems where fork() is pretty cheap).
fetch should be faster in most common cases, but probably not all.
fetches will be faster where client/server delta generation is
the bottleneck and not bandwidth. Of course, full-files are
generated server-side via deltas, too. Full files are always
transferred when they're updated, just like git-svnimport and
unlike command-line svn. I'm also hacking around memory leaks
(see comments) here by using some more forks.
I've tested fetch with http://, https://, file://, and svn://
repositories, so we should be reasonably covered in terms of
error handling for fetching.
Of course, we'll keep plain command-line svn compatibility as a
fallback for people running SVN 1.1 (I'm looking into library
support for 1.1.x SVN, too). If you want to force command-line
SVN usage, set GIT_SVN_NO_LIB=1 in your environment.
We also require two simultaneous connections (just like
git-svnimport), but this shouldn't be a problem for most
servers.
Less important commands:
show-ignore is slower because it requires repository
access, but -r/--revision <num> can be specified.
graft-branches may use more memory, but it's a
short-term process and is funky-filename-safe.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 1068 +++++++++++++++++++++++++---
contrib/git-svn/t/lib-git-svn.sh | 2
contrib/git-svn/t/t0000-contrib-git-svn.sh | 15
3 files changed, 974 insertions(+), 111 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 03416ae..9618c8b 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -31,6 +31,10 @@ use File::Path qw/mkpath/;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
use File::Spec qw//;
use POSIX qw/strftime/;
+
+my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
+$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
+libsvn_load();
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
@@ -74,7 +78,8 @@ my %cmd = (
'copy-similarity|C=i'=> \$_cp_similarity,
%fc_opts,
} ],
- 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { } ],
+ 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings",
+ { 'revision|r=i' => \$_revision } ],
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)",
{ 'no-ignore-externals' => \$_no_ignore_ext,
'upgrade' => \$_upgrade } ],
@@ -211,6 +216,8 @@ sub rebuild {
$newest_rev = $rev if ($rev > $newest_rev);
}
close $rev_list or croak $?;
+
+ goto out if $_use_lib;
if (!chdir $SVN_WC) {
svn_cmd_checkout($SVN_URL, $latest, $SVN_WC);
chdir $SVN_WC or croak $!;
@@ -228,7 +235,7 @@ sub rebuild {
}
waitpid $pid, 0;
croak $? if $?;
-
+out:
if ($_upgrade) {
print STDERR <<"";
Keeping deprecated refs/head/$GIT_SVN-HEAD for now. Please remove it
@@ -251,9 +258,18 @@ sub init {
}
sub fetch {
- my (@parents) = @_;
check_upgrade_needed();
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
+ my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_);
+ if ($ret->{commit} && quiet_run(qw(git-rev-parse --verify
+ refs/heads/master^0))) {
+ sys(qw(git-update-ref refs/heads/master),$ret->{commit});
+ }
+ return $ret;
+}
+
+sub fetch_cmd {
+ my (@parents) = @_;
my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL);
unless ($_revision) {
$_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD';
@@ -301,13 +317,91 @@ sub fetch {
$last_commit = git_commit($log_msg, $last_commit, @parents);
$last = $log_msg;
}
- unless (-e "$GIT_DIR/refs/heads/master") {
- sys(qw(git-update-ref refs/heads/master),$last_commit);
- }
close $svn_log->{fh};
+ $last->{commit} = $last_commit;
return $last;
}
+sub fetch_lib {
+ my (@parents) = @_;
+ $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
+ my $repo;
+ ($repo, $SVN_PATH) = repo_path_split($SVN_URL);
+ $SVN_LOG ||= libsvn_connect($repo);
+ $SVN ||= libsvn_connect($repo);
+ my ($last_rev, $last_commit) = svn_grab_base_rev();
+ my ($base, $head) = libsvn_parse_revision($last_rev);
+ if ($base > $head) {
+ return { revision => $last_rev, commit => $last_commit }
+ }
+ my $index = set_index($GIT_SVN_INDEX);
+
+ # limit ourselves and also fork() since get_log won't release memory
+ # after processing a revision and SVN stuff seems to leak
+ my $inc = 1000;
+ my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc);
+ read_uuid();
+ if (defined $last_commit) {
+ unless (-e $GIT_SVN_INDEX) {
+ sys(qw/git-read-tree/, $last_commit);
+ }
+ chomp (my $x = `git-write-tree`);
+ my ($y) = (`git-cat-file commit $last_commit`
+ =~ /^tree ($sha1)/m);
+ if ($y ne $x) {
+ unlink $GIT_SVN_INDEX or croak $!;
+ sys(qw/git-read-tree/, $last_commit);
+ }
+ chomp ($x = `git-write-tree`);
+ if ($y ne $x) {
+ print STDERR "trees ($last_commit) $y != $x\n",
+ "Something is seriously wrong...\n";
+ }
+ }
+ while (1) {
+ # fork, because using SVN::Pool with get_log() still doesn't
+ # seem to help enough to keep memory usage down.
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ $SVN::Error::handler = \&libsvn_skip_unknown_revs;
+ print "Fetching revisions $min .. $max\n";
+
+ # Yes I'm perfectly aware that the fourth argument
+ # below is the limit revisions number. Unfortunately
+ # performance sucks with it enabled, so it's much
+ # faster to fetch revision ranges instead of relying
+ # on the limiter.
+ $SVN_LOG->get_log( '/'.$SVN_PATH, $min, $max, 0, 1, 1,
+ sub {
+ my $log_msg;
+ if ($last_commit) {
+ $log_msg = libsvn_fetch(
+ $last_commit, @_);
+ $last_commit = git_commit(
+ $log_msg,
+ $last_commit,
+ @parents);
+ } else {
+ $log_msg = libsvn_new_tree(@_);
+ $last_commit = git_commit(
+ $log_msg, @parents);
+ }
+ });
+ $SVN::Error::handler = sub { 'quiet warnings' };
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ ($last_rev, $last_commit) = svn_grab_base_rev();
+ last if ($max >= $head);
+ $min = $max + 1;
+ $max += $inc;
+ $max = $head if ($max > $head);
+ }
+ restore_index($index);
+ return { revision => $last_rev, commit => $last_commit };
+}
+
sub commit {
my (@commits) = @_;
check_upgrade_needed();
@@ -332,6 +426,12 @@ sub commit {
}
}
chomp @revs;
+ $_use_lib ? commit_lib(@revs) : commit_cmd(@revs);
+ print "Done committing ",scalar @revs," revisions to SVN\n";
+}
+
+sub commit_cmd {
+ my (@revs) = @_;
chdir $SVN_WC or croak "Unable to chdir $SVN_WC: $!\n";
my $info = svn_info('.');
@@ -353,17 +453,95 @@ sub commit {
}
$svn_current_rev = svn_commit_tree($svn_current_rev, $c);
}
- print "Done committing ",scalar @revs," revisions to SVN\n";
}
-sub show_ignore {
- require File::Find or die $!;
- my $exclude_file = "$GIT_DIR/info/exclude";
- open my $fh, '<', $exclude_file or croak $!;
- chomp(my @excludes = (<$fh>));
- close $fh or croak $!;
+sub commit_lib {
+ my (@revs) = @_;
+ my ($r_last, $cmt_last) = svn_grab_base_rev();
+ defined $r_last or die "Must have an existing revision to commit\n";
+ my $fetched = fetch_lib();
+ if ($r_last != $fetched->{revision}) {
+ print STDERR "There are new revisions that were fetched ",
+ "and need to be merged (or acknowledged) ",
+ "before committing.\n",
+ "last rev: $r_last\n",
+ " current: $fetched->{revision}\n";
+ exit 1;
+ }
+ read_uuid();
+ my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
+ my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
+
+ foreach my $c (@revs) {
+ # fork for each commit because there's a memory leak I
+ # can't track down... (it's probably in the SVN code)
+ defined(my $pid = open my $fh, '-|') or croak $!;
+ if (!$pid) {
+ if (defined $LC_ALL) {
+ $ENV{LC_ALL} = $LC_ALL;
+ } else {
+ delete $ENV{LC_ALL};
+ }
+ my $log_msg = get_commit_message($c, $commit_msg);
+ my $ed = SVN::Git::Editor->new(
+ { r => $r_last,
+ ra => $SVN,
+ c => $c,
+ svn_path => $SVN_PATH
+ },
+ $SVN->get_commit_editor(
+ $log_msg->{msg},
+ sub {
+ libsvn_commit_cb(
+ @_, $c,
+ $log_msg->{msg},
+ $r_last,
+ $cmt_last)
+ },
+ @lock)
+ );
+ my $mods = libsvn_checkout_tree($r_last, $c, $ed);
+ if (@$mods == 0) {
+ print "No changes\nr$r_last = $cmt_last\n";
+ $ed->abort_edit;
+ } else {
+ $ed->close_edit;
+ }
+ exit 0;
+ }
+ my ($r_new, $cmt_new, $no);
+ while (<$fh>) {
+ print $_;
+ chomp;
+ if (/^r(\d+) = ($sha1)$/o) {
+ ($r_new, $cmt_new) = ($1, $2);
+ } elsif ($_ eq 'No changes') {
+ $no = 1;
+ }
+ }
+ close $fh or croak $!;
+ if (! defined $r_new && ! defined $cmt_new) {
+ unless ($no) {
+ die "Failed to parse revision information\n";
+ }
+ } else {
+ ($r_last, $cmt_last) = ($r_new, $cmt_new);
+ }
+ }
+ unlink $commit_msg;
+}
+sub show_ignore {
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
+ $_use_lib ? show_ignore_lib() : show_ignore_cmd();
+}
+
+sub show_ignore_cmd {
+ require File::Find or die $!;
+ if (defined $_revision) {
+ die "-r/--revision option doesn't work unless the Perl SVN ",
+ "libraries are used\n";
+ }
chdir $SVN_WC or croak $!;
my %ign;
File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
@@ -380,6 +558,14 @@ sub show_ignore {
}
}
+sub show_ignore_lib {
+ my $repo;
+ ($repo, $SVN_PATH) = repo_path_split($SVN_URL);
+ $SVN ||= libsvn_connect($repo);
+ my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
+ libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r);
+}
+
sub graft_branches {
my $gr_file = "$GIT_DIR/info/grafts";
my ($grafts, $comments) = read_grafts($gr_file);
@@ -403,7 +589,13 @@ sub graft_branches {
graft_merge_msg($grafts,$l_map,$u,$p);
}
}
- graft_file_copy($grafts,$l_map,$u) unless $_no_graft_copy;
+ unless ($_no_graft_copy) {
+ if ($_use_lib) {
+ graft_file_copy_lib($grafts,$l_map,$u);
+ } else {
+ graft_file_copy_cmd($grafts,$l_map,$u);
+ }
+ }
}
write_grafts($grafts, $comments, $gr_file);
@@ -574,7 +766,8 @@ sub complete_url_ls_init {
}
$var = $url . $var;
}
- chomp(my @ls = safe_qx(qw/svn ls --non-interactive/, $var));
+ chomp(my @ls = $_use_lib ? libsvn_ls_fullurl($var)
+ : safe_qx(qw/svn ls --non-interactive/, $var));
my $old = $GIT_SVN;
defined(my $pid = fork) or croak $!;
if (!$pid) {
@@ -617,7 +810,7 @@ sub common_prefix {
}
# this isn't funky-filename safe, but good enough for now...
-sub graft_file_copy {
+sub graft_file_copy_cmd {
my ($grafts, $l_map, $u) = @_;
my $paths = $l_map->{$u};
my $pfx = common_prefix([keys %$paths]);
@@ -625,7 +818,9 @@ sub graft_file_copy {
my $pid = open my $fh, '-|';
defined $pid or croak $!;
unless ($pid) {
- exec(qw/svn log -v/, $u.$pfx) or croak $!;
+ my @exec = qw/svn log -v/;
+ push @exec, "-r$_revision" if defined $_revision;
+ exec @exec, $u.$pfx or croak $!;
}
my ($r, $mp) = (undef, undef);
while (<$fh>) {
@@ -637,42 +832,40 @@ sub graft_file_copy {
} elsif (/^Changed paths:/) {
$mp = 1;
} elsif ($mp && m#^ [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) {
- my $dbg = "r$r | $_";
my ($p1, $p0, $r0) = ($1, $2, $3);
- my $c;
- foreach my $x (keys %$paths) {
- next unless ($p1 =~ /^\Q$x\E/);
- my $i = $paths->{$x};
- my $f = "$GIT_DIR/svn/$i/revs/$r";
- unless (-r $f) {
- print STDERR "r$r of $i not imported,",
- " $dbg\n";
- next;
- }
- $c = file_to_s($f);
- }
+ my $c = find_graft_path_commit($paths, $p1, $r);
next unless $c;
- foreach my $x (keys %$paths) {
- next unless ($p0 =~ /^\Q$x\E/);
- my $i = $paths->{$x};
- my $f = "$GIT_DIR/svn/$i/revs/$r0";
- while ($r0 && !-r $f) {
- # could be an older revision, too...
- $r0--;
- $f = "$GIT_DIR/svn/$i/revs/$r0";
- }
- unless (-r $f) {
- print STDERR "r$r0 of $i not imported,",
- " $dbg\n";
- next;
- }
- my $r1 = file_to_s($f);
- $grafts->{$c}->{$r1} = 1;
- }
+ find_graft_path_parents($grafts, $paths, $c, $p0, $r0);
}
}
}
+sub graft_file_copy_lib {
+ my ($grafts, $l_map, $u) = @_;
+ my $tree_paths = $l_map->{$u};
+ my $pfx = common_prefix([keys %$tree_paths]);
+ my ($repo, $path) = repo_path_split($u.$pfx);
+ $SVN_LOG ||= libsvn_connect($repo);
+ $SVN ||= libsvn_connect($repo);
+
+ my ($base, $head) = libsvn_parse_revision();
+ my $inc = 1000;
+ my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc);
+ while (1) {
+ my $pool = SVN::Pool->new;
+ $SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1,
+ sub {
+ libsvn_graft_file_copies($grafts, $tree_paths,
+ $path, @_);
+ }, $pool);
+ $pool->clear;
+ last if ($max >= $head);
+ $min = $max + 1;
+ $max += $inc;
+ $max = $head if ($max > $head);
+ }
+}
+
sub process_merge_msg_matches {
my ($grafts, $l_map, $u, $p, $c, @matches) = @_;
my (@strong, @weak);
@@ -734,9 +927,15 @@ sub graft_merge_msg {
sub read_uuid {
return if $SVN_UUID;
- my $info = shift || svn_info('.');
- $SVN_UUID = $info->{'Repository UUID'} or
+ if ($_use_lib) {
+ my $pool = SVN::Pool->new;
+ $SVN_UUID = $SVN->get_uuid($pool);
+ $pool->clear;
+ } else {
+ my $info = shift || svn_info('.');
+ $SVN_UUID = $info->{'Repository UUID'} or
croak "Repository UUID unreadable\n";
+ }
s_to_file($SVN_UUID,"$GIT_SVN_DIR/info/uuid");
}
@@ -769,9 +968,19 @@ sub repo_path_split {
$path =~ s#^/+##;
my @paths = split(m#/+#, $path);
- while (quiet_run(qw/svn ls --non-interactive/, $url)) {
- my $n = shift @paths || last;
- $url .= "/$n";
+ if ($_use_lib) {
+ while (1) {
+ $SVN = libsvn_connect($url);
+ last if (defined $SVN &&
+ defined eval { $SVN->get_latest_revnum });
+ my $n = shift @paths || last;
+ $url .= "/$n";
+ }
+ } else {
+ while (quiet_run(qw/svn ls --non-interactive/, $url)) {
+ my $n = shift @paths || last;
+ $url .= "/$n";
+ }
}
push @repo_path_split_cache, qr/^(\Q$url\E)/;
$path = join('/',@paths);
@@ -797,6 +1006,7 @@ sub setup_git_svn {
}
sub assert_svn_wc_clean {
+ return if $_use_lib;
my ($svn_rev) = @_;
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
my $lcr = svn_info('.')->{'Last Changed Rev'};
@@ -819,7 +1029,7 @@ sub assert_svn_wc_clean {
}
}
-sub assert_tree {
+sub get_tree_from_treeish {
my ($treeish) = @_;
croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o;
chomp(my $type = `git-cat-file -t $treeish`);
@@ -836,20 +1046,22 @@ sub assert_tree {
} else {
die "$treeish is a $type, expected tree, tag or commit\n";
}
+ return $expected;
+}
+
+sub assert_tree {
+ return if $_use_lib;
+ my ($treeish) = @_;
+ my $expected = get_tree_from_treeish($treeish);
- my $old_index = $ENV{GIT_INDEX_FILE};
my $tmpindex = $GIT_SVN_INDEX.'.assert-tmp';
if (-e $tmpindex) {
unlink $tmpindex or croak $!;
}
- $ENV{GIT_INDEX_FILE} = $tmpindex;
+ my $old_index = set_index($tmpindex);
index_changes(1);
chomp(my $tree = `git-write-tree`);
- if ($old_index) {
- $ENV{GIT_INDEX_FILE} = $old_index;
- } else {
- delete $ENV{GIT_INDEX_FILE};
- }
+ restore_index($old_index);
if ($tree ne $expected) {
croak "Tree mismatch, Got: $tree, Expected: $expected\n";
}
@@ -987,7 +1199,8 @@ sub precommit_check {
}
}
-sub svn_checkout_tree {
+
+sub get_diff {
my ($svn_rev, $treeish) = @_;
my $from = file_to_s("$REV_DIR/$svn_rev");
assert_tree($from);
@@ -1005,11 +1218,13 @@ sub svn_checkout_tree {
push @diff_tree, "-l$_l" if defined $_l;
exec(@diff_tree, $from, $treeish) or croak $!;
}
- my $mods = parse_diff_tree($diff_fh);
- unless (@$mods) {
- # git can do empty commits, but SVN doesn't allow it...
- return $mods;
- }
+ return parse_diff_tree($diff_fh);
+}
+
+sub svn_checkout_tree {
+ my ($svn_rev, $treeish) = @_;
+ my $mods = get_diff($svn_rev, $treeish);
+ return $mods unless (scalar @$mods);
my ($rm, $add) = precommit_check($mods);
my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
@@ -1052,6 +1267,23 @@ sub svn_checkout_tree {
return $mods;
}
+sub libsvn_checkout_tree {
+ my ($svn_rev, $treeish, $ed) = @_;
+ my $mods = get_diff($svn_rev, $treeish);
+ return $mods unless (scalar @$mods);
+ my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
+ foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
+ my $f = $m->{chg};
+ if (defined $o{$f}) {
+ $ed->$f($m);
+ } else {
+ croak "Invalid change type: $f\n";
+ }
+ }
+ $ed->rmdirs if $_rmdir;
+ return $mods;
+}
+
# svn ls doesn't work with respect to the current working tree, but what's
# in the repository. There's not even an option for it... *sigh*
# (added files don't show up and removed files remain in the ls listing)
@@ -1090,12 +1322,12 @@ sub handle_rmdir {
}
}
-sub svn_commit_tree {
- my ($svn_rev, $commit) = @_;
- my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
+sub get_commit_message {
+ my ($commit, $commit_msg) = (@_);
my %log_msg = ( msg => '' );
open my $msg, '>', $commit_msg or croak $!;
+ print "commit: $commit\n";
chomp(my $type = `git-cat-file -t $commit`);
if ($type eq 'commit') {
my $pid = open my $msg_fh, '-|';
@@ -1129,7 +1361,14 @@ sub svn_commit_tree {
{ local $/; chomp($log_msg{msg} = <$msg>); }
close $msg or croak $!;
- my ($oneline) = ($log_msg{msg} =~ /([^\n\r]+)/);
+ return \%log_msg;
+}
+
+sub svn_commit_tree {
+ my ($svn_rev, $commit) = @_;
+ my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
+ my $log_msg = get_commit_message($commit, $commit_msg);
+ my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/);
print "Committing $commit: $oneline\n";
if (defined $LC_ALL) {
@@ -1165,12 +1404,12 @@ sub svn_commit_tree {
/(\d{4})\-(\d\d)\-(\d\d)\s
(\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
or croak "Failed to parse date: $date\n";
- $log_msg{date} = "$tz $Y-$m-$d $H:$M:$S";
- $log_msg{author} = $info->{'Last Changed Author'};
- $log_msg{revision} = $committed;
- $log_msg{msg} .= "\n";
+ $log_msg->{date} = "$tz $Y-$m-$d $H:$M:$S";
+ $log_msg->{author} = $info->{'Last Changed Author'};
+ $log_msg->{revision} = $committed;
+ $log_msg->{msg} .= "\n";
my $parent = file_to_s("$REV_DIR/$svn_rev");
- git_commit(\%log_msg, $parent, $commit);
+ git_commit($log_msg, $parent, $commit);
return $committed;
}
# resync immediately
@@ -1335,8 +1574,14 @@ sub eol_cp {
binmode $rfd or croak $!;
open my $wfd, '>', $to or croak $!;
binmode $wfd or croak $!;
+ eol_cp_fd($rfd, $wfd, $es);
+ close $rfd or croak $!;
+ close $wfd or croak $!;
+}
- my $eol = $EOL{$es} or undef;
+sub eol_cp_fd {
+ my ($rfd, $wfd, $es) = @_;
+ my $eol = defined $es ? $EOL{$es} : undef;
my $buf;
use bytes;
while (1) {
@@ -1396,6 +1641,7 @@ sub do_update_index {
}
sub index_changes {
+ return if $_use_lib;
my $no_text_base = shift;
do_update_index([qw/git-diff-files --name-only -z/],
'remove',
@@ -1459,63 +1705,59 @@ sub assert_revision_eq_or_unknown {
sub git_commit {
my ($log_msg, @parents) = @_;
assert_revision_unknown($log_msg->{revision});
- my $out_fh = IO::File->new_tmpfile or croak $!;
-
map_tree_joins() if (@_branch_from && !%tree_map);
+ my (@tmp_parents, @exec_parents, %seen_parent);
+ if (my $lparents = $log_msg->{parents}) {
+ @tmp_parents = @$lparents
+ }
# commit parents can be conditionally bound to a particular
# svn revision via: "svn_revno=commit_sha1", filter them out here:
- my @exec_parents;
foreach my $p (@parents) {
next unless defined $p;
if ($p =~ /^(\d+)=($sha1_short)$/o) {
if ($1 == $log_msg->{revision}) {
- push @exec_parents, $2;
+ push @tmp_parents, $2;
}
} else {
- push @exec_parents, $p if $p =~ /$sha1_short/o;
+ push @tmp_parents, $p if $p =~ /$sha1_short/o;
}
}
-
- my $pid = fork;
- defined $pid or croak $!;
- if ($pid == 0) {
- $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
+ my $tree = $log_msg->{tree};
+ if (!defined $tree) {
+ my $index = set_index($GIT_SVN_INDEX);
index_changes();
- chomp(my $tree = `git-write-tree`);
+ chomp($tree = `git-write-tree`);
croak $? if $?;
- if (exists $tree_map{$tree}) {
- my %seen_parent = map { $_ => 1 } @exec_parents;
- foreach (@{$tree_map{$tree}}) {
- # MAXPARENT is defined to 16 in commit-tree.c:
- if ($seen_parent{$_} || @exec_parents > 16) {
- next;
- }
- push @exec_parents, $_;
- $seen_parent{$_} = 1;
- }
- }
+ restore_index($index);
+ }
+ if (exists $tree_map{$tree}) {
+ push @tmp_parents, @{$tree_map{$tree}};
+ }
+ foreach (@tmp_parents) {
+ next if $seen_parent{$_};
+ $seen_parent{$_} = 1;
+ push @exec_parents, $_;
+ # MAXPARENT is defined to 16 in commit-tree.c:
+ last if @exec_parents > 16;
+ }
+
+ defined(my $pid = open my $out_fh, '-|') or croak $!;
+ if ($pid == 0) {
my $msg_fh = IO::File->new_tmpfile or croak $!;
print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ",
"$SVN_URL\@$log_msg->{revision}",
" $SVN_UUID\n" or croak $!;
$msg_fh->flush == 0 or croak $!;
seek $msg_fh, 0, 0 or croak $!;
-
set_commit_env($log_msg);
-
my @exec = ('git-commit-tree',$tree);
push @exec, '-p', $_ foreach @exec_parents;
open STDIN, '<&', $msg_fh or croak $!;
- open STDOUT, '>&', $out_fh or croak $!;
exec @exec or croak $!;
}
- waitpid($pid,0);
- croak $? if $?;
-
- $out_fh->flush == 0 or croak $!;
- seek $out_fh, 0, 0 or croak $!;
chomp(my $commit = do { local $/; <$out_fh> });
+ close $out_fh or croak $?;
if ($commit !~ /^$sha1$/o) {
croak "Failed to commit, invalid sha1: $commit\n";
}
@@ -1534,6 +1776,7 @@ sub git_commit {
}
sys(@update_ref);
sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit);
+ # this output is read via pipe, do not change:
print "r$log_msg->{revision} = $commit\n";
if ($_repack && (--$_repack_nr == 0)) {
$_repack_nr = $_repack;
@@ -1545,6 +1788,9 @@ sub git_commit {
sub set_commit_env {
my ($log_msg) = @_;
my $author = $log_msg->{author};
+ if (!defined $author || length $author == 0) {
+ $author = '(no author)';
+ }
my ($name,$email) = defined $users{$author} ? @{$users{$author}}
: ($author,"$author\@$SVN_UUID");
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
@@ -2029,6 +2275,612 @@ sub show_commit_normal {
}
}
+sub libsvn_load {
+ return unless $_use_lib;
+ $_use_lib = eval {
+ require SVN::Core;
+ if ($SVN::Core::VERSION lt '1.2.1') {
+ die "Need SVN::Core 1.2.1 or better ",
+ "(got $SVN::Core::VERSION) ",
+ "Falling back to command-line svn\n";
+ }
+ require SVN::Ra;
+ require SVN::Delta;
+ push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
+ my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
+ $SVN::Node::dir.$SVN::Node::unknown.
+ $SVN::Node::none.$SVN::Node::file.
+ $SVN::Node::dir.$SVN::Node::unknown;
+ 1;
+ };
+}
+
+sub libsvn_connect {
+ my ($url) = @_;
+ my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(),
+ SVN::Client::get_ssl_server_trust_file_provider(),
+ SVN::Client::get_username_provider()]);
+ my $s = eval { SVN::Ra->new(url => $url, auth => $auth) };
+ return $s;
+}
+
+sub libsvn_get_file {
+ my ($gui, $f, $rev) = @_;
+ my $p = $f;
+ return unless ($p =~ s#^\Q$SVN_PATH\E/?##);
+
+ my $fd = IO::File->new_tmpfile or croak $!;
+ my $pool = SVN::Pool->new;
+ my ($r, $props) = $SVN->get_file($f, $rev, $fd, $pool);
+ $pool->clear;
+ $fd->flush == 0 or croak $!;
+ seek $fd, 0, 0 or croak $!;
+ if (my $es = $props->{'svn:eol-style'}) {
+ my $new_fd = IO::File->new_tmpfile or croak $!;
+ eol_cp_fd($fd, $new_fd, $es);
+ close $fd or croak $!;
+ $fd = $new_fd;
+ seek $fd, 0, 0 or croak $!;
+ $fd->flush == 0 or croak $!;
+ }
+ my $mode = '100644';
+ if (exists $props->{'svn:executable'}) {
+ $mode = '100755';
+ }
+ if (exists $props->{'svn:special'}) {
+ $mode = '120000';
+ local $/;
+ my $link = <$fd>;
+ $link =~ s/^link // or die "svn:special file with contents: <",
+ $link, "> is not understood\n";
+ seek $fd, 0, 0 or croak $!;
+ truncate $fd, 0 or croak $!;
+ print $fd $link or croak $!;
+ seek $fd, 0, 0 or croak $!;
+ $fd->flush == 0 or croak $!;
+ }
+ my $pid = open my $ho, '-|';
+ defined $pid or croak $!;
+ if (!$pid) {
+ open STDIN, '<&', $fd or croak $!;
+ exec qw/git-hash-object -w --stdin/ or croak $!;
+ }
+ chomp(my $hash = do { local $/; <$ho> });
+ close $ho or croak $?;
+ $hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
+ print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!;
+ close $fd or croak $!;
+}
+
+sub libsvn_log_entry {
+ my ($rev, $author, $date, $msg, $parents) = @_;
+ my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
+ (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
+ or die "Unable to parse date: $date\n";
+ if (defined $_authors && ! defined $users{$author}) {
+ die "Author: $author not defined in $_authors file\n";
+ }
+ return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
+ author => $author, msg => $msg."\n", parents => $parents || [] }
+}
+
+sub process_rm {
+ my ($gui, $last_commit, $f) = @_;
+ $f =~ s#^\Q$SVN_PATH\E/?## or return;
+ # remove entire directories.
+ if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
+ defined(my $pid = open my $ls, '-|') or croak $!;
+ if (!$pid) {
+ exec(qw/git-ls-tree -r --name-only -z/,
+ $last_commit,'--',$f) or croak $!;
+ }
+ local $/ = "\0";
+ while (<$ls>) {
+ print $gui '0 ',0 x 40,"\t",$_ or croak $!;
+ }
+ close $ls or croak $!;
+ } else {
+ print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
+ }
+}
+
+sub libsvn_fetch {
+ my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
+ open my $gui, '| git-update-index -z --index-info' or croak $!;
+ my @amr;
+ foreach my $f (keys %$paths) {
+ my $m = $paths->{$f}->action();
+ $f =~ s#^/+##;
+ if ($m =~ /^[DR]$/) {
+ process_rm($gui, $last_commit, $f);
+ next if $m eq 'D';
+ # 'R' can be file replacements, too, right?
+ }
+ my $pool = SVN::Pool->new;
+ my $t = $SVN->check_path($f, $rev, $pool);
+ if ($t == $SVN::Node::file) {
+ if ($m =~ /^[AMR]$/) {
+ push @amr, $f;
+ } else {
+ die "Unrecognized action: $m, ($f r$rev)\n";
+ }
+ }
+ $pool->clear;
+ }
+ libsvn_get_file($gui, $_, $rev) foreach (@amr);
+ close $gui or croak $!;
+ return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
+}
+
+sub svn_grab_base_rev {
+ defined(my $pid = open my $fh, '-|') or croak $!;
+ if (!$pid) {
+ open my $null, '>', '/dev/null' or croak $!;
+ open STDERR, '>&', $null or croak $!;
+ exec qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0"
+ or croak $!;
+ }
+ chomp(my $c = do { local $/; <$fh> });
+ close $fh;
+ if (defined $c && length $c) {
+ my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /,
+ safe_qx(qw/git-cat-file commit/, $c)))[0]);
+ return ($rev, $c);
+ }
+ return (undef, undef);
+}
+
+sub libsvn_parse_revision {
+ my $base = shift;
+ my $head = $SVN->get_latest_revnum();
+ if (!defined $_revision || $_revision eq 'BASE:HEAD') {
+ return ($base + 1, $head) if (defined $base);
+ return (0, $head);
+ }
+ return ($1, $2) if ($_revision =~ /^(\d+):(\d+)$/);
+ return ($_revision, $_revision) if ($_revision =~ /^\d+$/);
+ if ($_revision =~ /^BASE:(\d+)$/) {
+ return ($base + 1, $1) if (defined $base);
+ return (0, $head);
+ }
+ return ($1, $head) if ($_revision =~ /^(\d+):HEAD$/);
+ die "revision argument: $_revision not understood by git-svn\n",
+ "Try using the command-line svn client instead\n";
+}
+
+sub libsvn_traverse {
+ my ($gui, $pfx, $path, $rev) = @_;
+ my $cwd = "$pfx/$path";
+ my $pool = SVN::Pool->new;
+ $cwd =~ s#^/+##g;
+ my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
+ foreach my $d (keys %$dirent) {
+ my $t = $dirent->{$d}->kind;
+ if ($t == $SVN::Node::dir) {
+ libsvn_traverse($gui, $cwd, $d, $rev);
+ } elsif ($t == $SVN::Node::file) {
+ libsvn_get_file($gui, "$cwd/$d", $rev);
+ }
+ }
+ $pool->clear;
+}
+
+sub libsvn_traverse_ignore {
+ my ($fh, $path, $r) = @_;
+ $path =~ s#^/+##g;
+ my $pool = SVN::Pool->new;
+ my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
+ my $p = $path;
+ $p =~ s#^\Q$SVN_PATH\E/?##;
+ print $fh length $p ? "\n# $p\n" : "\n# /\n";
+ if (my $s = $props->{'svn:ignore'}) {
+ $s =~ s/[\r\n]+/\n/g;
+ chomp $s;
+ if (length $p == 0) {
+ $s =~ s#\n#\n/$p#g;
+ print $fh "/$s\n";
+ } else {
+ $s =~ s#\n#\n/$p/#g;
+ print $fh "/$p/$s\n";
+ }
+ }
+ foreach (sort keys %$dirent) {
+ next if $dirent->{$_}->kind != $SVN::Node::dir;
+ libsvn_traverse_ignore($fh, "$path/$_", $r);
+ }
+ $pool->clear;
+}
+
+sub libsvn_new_tree {
+ my ($paths, $rev, $author, $date, $msg) = @_;
+ my $svn_path = '/'.$SVN_PATH;
+
+ # look for a parent from another branch:
+ foreach (keys %$paths) {
+ next if ($_ ne $svn_path);
+ my $i = $paths->{$_};
+ my $branch_from = $i->copyfrom_path or next;
+ my $r = $i->copyfrom_rev;
+ print STDERR "Found possible branch point: ",
+ "$branch_from => $svn_path, $r\n";
+ $branch_from =~ s#^/##;
+ my $l_map = read_url_paths();
+ my $url = $SVN->{url};
+ defined $l_map->{$url} or next;
+ my $id = $l_map->{$url}->{$branch_from} or next;
+ my $f = "$GIT_DIR/svn/$id/revs/$r";
+ while ($r && !-r $f) {
+ $r--;
+ $f = "$GIT_DIR/svn/$id/revs/$r";
+ }
+ if (-r $f) {
+ my $parent = file_to_s($f);
+ unlink $GIT_SVN_INDEX;
+ print STDERR "Found branch parent: $parent\n";
+ sys(qw/git-read-tree/, $parent);
+ return libsvn_fetch($parent, $paths, $rev,
+ $author, $date, $msg);
+ }
+ print STDERR "Nope, branch point not imported or unknown\n";
+ }
+ open my $gui, '| git-update-index -z --index-info' or croak $!;
+ my $pool = SVN::Pool->new;
+ libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool);
+ $pool->clear;
+ close $gui or croak $!;
+ return libsvn_log_entry($rev, $author, $date, $msg);
+}
+
+sub find_graft_path_commit {
+ my ($tree_paths, $p1, $r1) = @_;
+ foreach my $x (keys %$tree_paths) {
+ next unless ($p1 =~ /^\Q$x\E/);
+ my $i = $tree_paths->{$x};
+ my $f = "$GIT_DIR/svn/$i/revs/$r1";
+
+ return file_to_s($f) if (-r $f);
+
+ print STDERR "r$r1 of $i not imported\n";
+ next;
+ }
+ return undef;
+}
+
+sub find_graft_path_parents {
+ my ($grafts, $tree_paths, $c, $p0, $r0) = @_;
+ foreach my $x (keys %$tree_paths) {
+ next unless ($p0 =~ /^\Q$x\E/);
+ my $i = $tree_paths->{$x};
+ my $f = "$GIT_DIR/svn/$i/revs/$r0";
+ while ($r0 && !-r $f) {
+ # could be an older revision, too...
+ $r0--;
+ $f = "$GIT_DIR/svn/$i/revs/$r0";
+ }
+ unless (-r $f) {
+ print STDERR "r$r0 of $i not imported\n";
+ next;
+ }
+ my $parent = file_to_s($f);
+ $grafts->{$c}->{$parent} = 1;
+ }
+}
+
+sub libsvn_graft_file_copies {
+ my ($grafts, $tree_paths, $path, $paths, $rev) = @_;
+ foreach (keys %$paths) {
+ my $i = $paths->{$_};
+ my ($m, $p0, $r0) = ($i->action, $i->copyfrom_path,
+ $i->copyfrom_rev);
+ next unless (defined $p0 && defined $r0);
+
+ my $p1 = $_;
+ $p1 =~ s#^/##;
+ $p0 =~ s#^/##;
+ my $c = find_graft_path_commit($tree_paths, $p1, $rev);
+ next unless $c;
+ find_graft_path_parents($grafts, $tree_paths, $c, $p0, $r0);
+ }
+}
+
+sub set_index {
+ my $old = $ENV{GIT_INDEX_FILE};
+ $ENV{GIT_INDEX_FILE} = shift;
+ return $old;
+}
+
+sub restore_index {
+ my ($old) = @_;
+ if (defined $old) {
+ $ENV{GIT_INDEX_FILE} = $old;
+ } else {
+ delete $ENV{GIT_INDEX_FILE};
+ }
+}
+
+sub libsvn_commit_cb {
+ my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_;
+ if ($rev == ($r_last + 1)) {
+ # optimized (avoid fetch)
+ my $log = libsvn_log_entry($rev,$committer,$date,$msg);
+ $log->{tree} = get_tree_from_treeish($c);
+ my $cmt = git_commit($log, $cmt_last, $c);
+ my @diff = safe_qx('git-diff-tree', $cmt, $c);
+ if (@diff) {
+ print STDERR "Trees differ: $cmt $c\n",
+ join('',@diff),"\n";
+ exit 1;
+ }
+ } else {
+ fetch_lib("$rev=$c");
+ }
+}
+
+sub libsvn_ls_fullurl {
+ my $fullurl = shift;
+ my ($repo, $path) = repo_path_split($fullurl);
+ $SVN ||= libsvn_connect($repo);
+ my @ret;
+ my $pool = SVN::Pool->new;
+ my ($dirent, undef, undef) = $SVN->get_dir($path,
+ $SVN->get_latest_revnum, $pool);
+ foreach my $d (keys %$dirent) {
+ if ($dirent->{$d}->kind == $SVN::Node::dir) {
+ push @ret, "$d/"; # add '/' for compat with cli svn
+ }
+ }
+ $pool->clear;
+ return @ret;
+}
+
+
+sub libsvn_skip_unknown_revs {
+ my $err = shift;
+ my $errno = $err->apr_err();
+ # Maybe the branch we're tracking didn't
+ # exist when the repo started, so it's
+ # not an error if it doesn't, just continue
+ #
+ # Wonderfully consistent library, eh?
+ # 160013 - svn:// and file://
+ # 175002 - http(s)://
+ # More codes may be discovered later...
+ if ($errno == 175002 || $errno == 160013) {
+ print STDERR "directory non-existent\n";
+ return;
+ }
+ croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
+};
+
+package SVN::Git::Editor;
+use vars qw/@ISA/;
+use strict;
+use warnings;
+use Carp qw/croak/;
+use IO::File;
+
+sub new {
+ my $class = shift;
+ my $git_svn = shift;
+ my $self = SVN::Delta::Editor->new(@_);
+ bless $self, $class;
+ foreach (qw/svn_path c r ra /) {
+ die "$_ required!\n" unless (defined $git_svn->{$_});
+ $self->{$_} = $git_svn->{$_};
+ }
+ $self->{pool} = SVN::Pool->new;
+ $self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) };
+ $self->{rm} = { };
+ require Digest::MD5;
+ return $self;
+}
+
+sub split_path {
+ return ($_[0] =~ m#^(.*?)/?([^/]+)$#);
+}
+
+sub repo_path {
+ (defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]"
+ : $_[0]->{svn_path}
+}
+
+sub url_path {
+ my ($self, $path) = @_;
+ $self->{ra}->{url} . '/' . $self->repo_path($path);
+}
+
+sub rmdirs {
+ my ($self) = @_;
+ my $rm = $self->{rm};
+ delete $rm->{''}; # we never delete the url we're tracking
+ return unless %$rm;
+
+ foreach (keys %$rm) {
+ my @d = split m#/#, $_;
+ my $c = shift @d;
+ $rm->{$c} = 1;
+ while (@d) {
+ $c .= '/' . shift @d;
+ $rm->{$c} = 1;
+ }
+ }
+ delete $rm->{$self->{svn_path}};
+ delete $rm->{''}; # we never delete the url we're tracking
+ return unless %$rm;
+
+ defined(my $pid = open my $fh,'-|') or croak $!;
+ if (!$pid) {
+ exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
+ }
+ local $/ = "\0";
+ while (<$fh>) {
+ chomp;
+ $_ = $self->{svn_path} . '/' . $_;
+ my ($dn) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#);
+ delete $rm->{$dn};
+ last unless %$rm;
+ }
+ my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
+ foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
+ $self->close_directory($bat->{$d}, $p);
+ my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
+ $self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
+ delete $bat->{$d};
+ }
+}
+
+sub open_or_add_dir {
+ my ($self, $full_path, $baton) = @_;
+ my $p = SVN::Pool->new;
+ my $t = $self->{ra}->check_path($full_path, $self->{r}, $p);
+ $p->clear;
+ if ($t == $SVN::Node::none) {
+ return $self->add_directory($full_path, $baton,
+ undef, -1, $self->{pool});
+ } elsif ($t == $SVN::Node::dir) {
+ return $self->open_directory($full_path, $baton,
+ $self->{r}, $self->{pool});
+ }
+ print STDERR "$full_path already exists in repository at ",
+ "r$self->{r} and it is not a directory (",
+ ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
+ exit 1;
+}
+
+sub ensure_path {
+ my ($self, $path) = @_;
+ my $bat = $self->{bat};
+ $path = $self->repo_path($path);
+ return $bat->{''} unless (length $path);
+ my @p = split m#/+#, $path;
+ my $c = shift @p;
+ $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''});
+ while (@p) {
+ my $c0 = $c;
+ $c .= '/' . shift @p;
+ $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0});
+ }
+ return $bat->{$c};
+}
+
+sub A {
+ my ($self, $m) = @_;
+ my ($dir, $file) = split_path($m->{file_b});
+ my $pbat = $self->ensure_path($dir);
+ my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+ undef, -1);
+ $self->chg_file($fbat, $m);
+ $self->close_file($fbat,undef,$self->{pool});
+}
+
+sub C {
+ my ($self, $m) = @_;
+ my ($dir, $file) = split_path($m->{file_b});
+ my $pbat = $self->ensure_path($dir);
+ my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+ $self->url_path($m->{file_a}), $self->{r});
+ $self->chg_file($fbat, $m);
+ $self->close_file($fbat,undef,$self->{pool});
+}
+
+sub delete_entry {
+ my ($self, $path, $pbat) = @_;
+ my $rpath = $self->repo_path($path);
+ my ($dir, $file) = split_path($rpath);
+ $self->{rm}->{$dir} = 1;
+ $self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool});
+}
+
+sub R {
+ my ($self, $m) = @_;
+ my ($dir, $file) = split_path($m->{file_b});
+ my $pbat = $self->ensure_path($dir);
+ my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+ $self->url_path($m->{file_a}), $self->{r});
+ $self->chg_file($fbat, $m);
+ $self->close_file($fbat,undef,$self->{pool});
+
+ ($dir, $file) = split_path($m->{file_a});
+ $pbat = $self->ensure_path($dir);
+ $self->delete_entry($m->{file_a}, $pbat);
+}
+
+sub M {
+ my ($self, $m) = @_;
+ my ($dir, $file) = split_path($m->{file_b});
+ my $pbat = $self->ensure_path($dir);
+ my $fbat = $self->open_file($self->repo_path($m->{file_b}),
+ $pbat,$self->{r},$self->{pool});
+ $self->chg_file($fbat, $m);
+ $self->close_file($fbat,undef,$self->{pool});
+}
+
+sub T { shift->M(@_) }
+
+sub change_file_prop {
+ my ($self, $fbat, $pname, $pval) = @_;
+ $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
+}
+
+sub chg_file {
+ my ($self, $fbat, $m) = @_;
+ if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
+ $self->change_file_prop($fbat,'svn:executable','*');
+ } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
+ $self->change_file_prop($fbat,'svn:executable',undef);
+ }
+ my $fh = IO::File->new_tmpfile or croak $!;
+ if ($m->{mode_b} =~ /^120/) {
+ print $fh 'link ' or croak $!;
+ $self->change_file_prop($fbat,'svn:special','*');
+ } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
+ $self->change_file_prop($fbat,'svn:special',undef);
+ }
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ open STDOUT, '>&', $fh or croak $!;
+ exec qw/git-cat-file blob/, $m->{sha1_b} or croak $!;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ $fh->flush == 0 or croak $!;
+ seek $fh, 0, 0 or croak $!;
+
+ my $md5 = Digest::MD5->new;
+ $md5->addfile($fh) or croak $!;
+ seek $fh, 0, 0 or croak $!;
+
+ my $exp = $md5->hexdigest;
+ my $atd = $self->apply_textdelta($fbat, undef, $self->{pool});
+ my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool});
+ die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
+
+ close $fh or croak $!;
+}
+
+sub D {
+ my ($self, $m) = @_;
+ my ($dir, $file) = split_path($m->{file_b});
+ my $pbat = $self->ensure_path($dir);
+ $self->delete_entry($m->{file_b}, $pbat);
+}
+
+sub close_edit {
+ my ($self) = @_;
+ my ($p,$bat) = ($self->{pool}, $self->{bat});
+ foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
+ $self->close_directory($bat->{$_}, $p);
+ }
+ $self->SUPER::close_edit($p);
+ $p->clear;
+}
+
+sub abort_edit {
+ my ($self) = @_;
+ $self->SUPER::abort_edit($self->{pool});
+ $self->{pool}->clear;
+}
+
__END__
Data structures:
@@ -2062,3 +2914,7 @@ diff-index line ($m hash)
file_b => new/current file name of a file (any chg)
}
;
+
+Notes:
+ I don't trust the each() function on unless I created %hash myself
+ because the internal iterator may not have started at base.
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
index 58408a6..2843258 100644
--- a/contrib/git-svn/t/lib-git-svn.sh
+++ b/contrib/git-svn/t/lib-git-svn.sh
@@ -11,7 +11,7 @@ fi
GIT_DIR=$PWD/.git
GIT_SVN_DIR=$GIT_DIR/svn/git-svn
-SVN_TREE=$GIT_SVN_DIR/tree
+SVN_TREE=$GIT_SVN_DIR/svn-tree
svnadmin >/dev/null 2>&1
if test $? != 1
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh
index 0c6ff20..c33b522 100644
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh
@@ -31,6 +31,7 @@ test_expect_success \
'import an SVN revision into git' \
'git-svn fetch'
+test_expect_success "checkout from svn" "svn co $svnrepo $SVN_TREE"
name='try a deep --rmdir with a commit'
git checkout -f -b mybranch remotes/git-svn
@@ -41,6 +42,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch &&
+ svn up $SVN_TREE &&
test -d $SVN_TREE/dir && test ! -d $SVN_TREE/dir/a"
@@ -52,7 +54,7 @@ git update-index --remove dir/file
git update-index --add dir/file/file
git commit -m "$name"
-test_expect_code 1 "$name" \
+test_expect_failure "$name" \
'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch' \
|| true
@@ -67,7 +69,7 @@ git update-index --remove -- bar/zzz
git update-index --add -- bar
git commit -m "$name"
-test_expect_code 1 "$name" \
+test_expect_failure "$name" \
'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch2' \
|| true
@@ -82,7 +84,7 @@ echo yyy > bar/zzz/yyy
git-update-index --add bar/zzz/yyy
git commit -m "$name"
-test_expect_code 1 "$name" \
+test_expect_failure "$name" \
'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch3' \
|| true
@@ -97,7 +99,7 @@ echo asdf > dir
git update-index --add -- dir
git commit -m "$name"
-test_expect_code 1 "$name" \
+test_expect_failure "$name" \
'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch4' \
|| true
@@ -111,6 +113,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+ svn up $SVN_TREE &&
test ! -x $SVN_TREE/exec.sh"
@@ -121,6 +124,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+ svn up $SVN_TREE &&
test -x $SVN_TREE/exec.sh"
@@ -133,6 +137,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+ svn up $SVN_TREE &&
test -L $SVN_TREE/exec.sh"
@@ -145,6 +150,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+ svn up $SVN_TREE &&
test -x $SVN_TREE/bar/zzz &&
test -L $SVN_TREE/exec-2.sh"
@@ -159,6 +165,7 @@ git commit -m "$name"
test_expect_success "$name" \
"git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+ svn up $SVN_TREE &&
test -f $SVN_TREE/exec-2.sh &&
test ! -L $SVN_TREE/exec-2.sh &&
diff -u help $SVN_TREE/exec-2.sh"
--
1.4.0
^ permalink raw reply related
* [PATCH 8/13] git-svn: add --shared and --template= options to pass to init-db
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index a04cf1d..d8f103e 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -35,6 +35,7 @@ my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity,
$_repack, $_repack_nr, $_repack_flags,
+ $_template, $_shared,
$_version, $_upgrade, $_authors, $_branch_all_refs);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
@@ -54,7 +55,9 @@ my %cmd = (
fetch => [ \&fetch, "Download new revisions from SVN",
{ 'revision|r=s' => \$_revision, %fc_opts } ],
init => [ \&init, "Initialize a repo for tracking" .
- " (requires URL argument)", { } ],
+ " (requires URL argument)",
+ { 'template=s' => \$_template,
+ 'shared' => \$_shared } ],
commit => [ \&commit, "Commit git revisions to SVN",
{ 'stdin|' => \$_stdin,
'edit|e' => \$_edit,
@@ -217,7 +220,10 @@ sub init {
$SVN_URL = shift or die "SVN repository location required " .
"as a command-line argument\n";
unless (-d $GIT_DIR) {
- sys('git-init-db');
+ my @init_db = ('git-init-db');
+ push @init_db, "--template=$_template" if defined $_template;
+ push @init_db, "--shared" if defined $_shared;
+ sys(@init_db);
}
setup_git_svn();
}
--
1.4.0
^ permalink raw reply related
* [PATCH 4/13] git-svn: support manually placed initial trees from fetch
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
Sometimes I don't feel like downloading an entire tree again when
I actually decide a branch is worth tracking, so some users can
get around it more easily with this.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index d4b9323..54f3d63 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -262,7 +262,14 @@ sub fetch {
} else {
chdir $SVN_WC or croak $!;
read_uuid();
- $last_commit = file_to_s("$REV_DIR/$base->{revision}");
+ eval { $last_commit = file_to_s("$REV_DIR/$base->{revision}") };
+ # looks like a user manually cp'd and svn switch'ed
+ unless ($last_commit) {
+ sys(qw/svn revert -R ./);
+ assert_svn_wc_clean($base->{revision});
+ $last_commit = git_commit($base, @parents);
+ assert_tree($last_commit);
+ }
}
my @svn_up = qw(svn up);
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
--
1.4.0
^ permalink raw reply related
* [PATCH 2/13] git-svn: --branch-all-refs / -B support
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
This should make life easier for all those who type:
`git-rev-parse --symbolic --all | xargs -n1 echo -b`
every time they run git-svn fetch.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 15 ++++++++++++++-
1 files changed, 14 insertions(+), 1 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 089d597..c91160d 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -34,12 +34,13 @@ my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity,
- $_version, $_upgrade, $_authors);
+ $_version, $_upgrade, $_authors, $_branch_all_refs);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'branch|b=s' => \@_branch_from,
+ 'branch-all-refs|B' => \$_branch_all_refs,
'authors-file|A=s' => \$_authors );
# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
@@ -108,6 +109,7 @@ usage(0) if $_help;
version() if $_version;
usage(1) unless defined $cmd;
load_authors() if $_authors;
+load_all_refs() if $_branch_all_refs;
svn_compat_check();
$cmd{$cmd}->[0]->(@ARGV);
exit 0;
@@ -1238,6 +1240,17 @@ sub map_tree_joins {
}
}
+sub load_all_refs {
+ if (@_branch_from) {
+ print STDERR '--branch|-b parameters are ignored when ',
+ "--branch-all-refs|-B is passed\n";
+ }
+
+ # don't worry about rev-list on non-commit objects/tags,
+ # it shouldn't blow up if a ref is a blob or tree...
+ chomp(@_branch_from = `git-rev-parse --symbolic --all`);
+}
+
# '<svn username> = real-name <email address>' mapping based on git-svnimport:
sub load_authors {
open my $authors, '<', $_authors or die "Can't open $_authors $!\n";
--
1.4.0
^ permalink raw reply related
* [PATCH 1/13] git-svn: support -C<num> passing to git-diff-tree
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
In-Reply-To: <11502217352245-git-send-email-normalperson@yhbt.net>
The repo-config key is 'svn.copysimilarity'
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
contrib/git-svn/git-svn.perl | 11 +++++++++--
1 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 72129de..089d597 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -33,7 +33,8 @@ use POSIX qw/strftime/;
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
- $_find_copies_harder, $_l, $_version, $_upgrade, $_authors);
+ $_find_copies_harder, $_l, $_cp_similarity,
+ $_version, $_upgrade, $_authors);
my (@_branch_from, %tree_map, %users);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
@@ -55,6 +56,7 @@ my %cmd = (
'rmdir' => \$_rmdir,
'find-copies-harder' => \$_find_copies_harder,
'l=i' => \$_l,
+ 'copy-similarity|C=i'=> \$_cp_similarity,
%fc_opts,
} ],
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { } ],
@@ -580,7 +582,12 @@ sub svn_checkout_tree {
my $pid = open my $diff_fh, '-|';
defined $pid or croak $!;
if ($pid == 0) {
- my @diff_tree = qw(git-diff-tree -z -r -C);
+ my @diff_tree = qw(git-diff-tree -z -r);
+ if ($_cp_similarity) {
+ push @diff_tree, "-C$_cp_similarity";
+ } else {
+ push @diff_tree, '-C';
+ }
push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
push @diff_tree, "-l$_l" if defined $_l;
exec(@diff_tree, $from, $treeish) or croak $!;
--
1.4.0
^ permalink raw reply related
* [PATCH 0/13] git-svn: better branch support, SVN:: lib usage, feature additions
From: Eric Wong @ 2006-06-13 18:02 UTC (permalink / raw)
To: git, Junio C Hamano
Most of these patches rely on the last series of patches I posted
(<11500094252972-git-send-email-normalperson@yhbt.net>).
[PATCH 1/13] git-svn: support -C<num> passing to git-diff-tree
[PATCH 2/13] git-svn: --branch-all-refs / -B support
[PATCH 3/13] git-svn: optimize --branch and --branch-all-ref
[PATCH 4/13] git-svn: support manually placed initial trees from fetch
These have been around since April or so, but I kept forgetting
about them since they were on a machine I rarely use.
All low impact changes.
[PATCH 5/13] git-svn: Move all git-svn-related paths into $GIT_DIR/svn
Compatibility with 1.0.0 is broken here, in preparation for later
changes and stuff.
[PATCH 6/13] git-svn: minor cleanups, extra error-checking
[PATCH 7/13] git-svn: add --repack and --repack-flags= options
[PATCH 8/13] git-svn: add --shared and --template= options to pass to init-db
Simple, low-impact changes.
[PATCH 9/13] git-svn: add some functionality to better support branches in svn
Three new commands, graft-branches being the most interesting.
Large patches, but low impact.
[PATCH 10/13] git-svn: add UTF-8 message test
Low impact change.
[PATCH 11/13] git-svn: add 'log' command, a facsimile of basic `svn log'
A simple convenience command. This should help ease communication
between git-svn and SVN users. Large patch but low impact.
These last two are very high impact changes. Some bugs were noticed
and fixed, but more may have been introduced.
[PATCH 12/13] git-svn: add support for Perl SVN::* libraries
We're faster, much faster in the common case. Some hacks to get
around memory leaks, but we're faster, and that matters to
people. There's a huge disk space reduction, too, since we
don't have to keep working copies around. Of course, this is
optional, command-line svn will continue to be supported.
[PATCH 13/13] git-svn: make the $GIT_DIR/svn/*/revs directory obsolete
Now that we're faster, we can import 100k revisions in a finite
amount of time. Which means lots of 41-byte files in one
directory: gross. Huge disk space reduction for large
histories.
--
Eric Wong
^ permalink raw reply
* Re: git-cvsimport doesn't quite work, wrt branches
From: Linus Torvalds @ 2006-06-13 17:20 UTC (permalink / raw)
To: Jim Meyering
Cc: Git Mailing List, Matthias Urlichs, Yann Dirson, Pavel Roskin
In-Reply-To: <87irn5ovn6.fsf@rho.meyering.net>
On Tue, 13 Jun 2006, Jim Meyering wrote:
>
> Here's a test case that shows how git-cvsimport is misbehaving.
> The script below demonstrates the problem with git-1.3.3 as
> well as with 1.4.0.rc2.g5e3a6. As for cvsps, I'm using version 2.1.
Well, it's a cvsps problem.
Big surprise.
Sadly, it also seems to be one that isn't fixed by the patches _I_ have,
and looking at Yann's set of patches, I don't think they fix it either.
This is what (my version of) CVSps reports for your repository:
---------------------
PatchSet 1
Date: 2006/06/13 10:06:42
Author: torvalds
Branch: HEAD
Tag: (none)
Log:
.
Members:
on-br:INITIAL->1.1
on-trunk:INITIAL->1.1
---------------------
PatchSet 2
Date: 2006/06/13 10:06:44
Author: torvalds
Branch: B
Ancestor branch: HEAD
Tag: (none)
Log:
.
Members:
on-br:1.1->1.1.2.1
---------------------
PatchSet 3
Date: 2006/06/13 10:06:46
Author: torvalds
Branch: HEAD
Tag: (none)
Log:
.
Members:
on-br:1.1->1.2(DEAD)
and note how the "on-br" file is part of the initial PatchSet 1.
So CVSps basically tells git-cvsimport that commit 2 (on branch B) is
based on commit 1, and doesn't say that "on-trunk" has gone away, so the
resulting git repository has branch B containing "on-trunk" version 1.1,
and "on-br" version 1.1.2.1.
CVS branches obviously sometimes confuse CVSps. Sadly, they also confuse
_me_, so I don't see how to fix this particular CVSps bug, because I'm as
confused as CVSps is ;)
We'd need to have CVSps tell git that the "on-trunk" file was never added
to branch B: the simplest way to do that would be to say that it has
become (DEAD) in PatchSet 2 (which is not technically true in CVS terms,
but _is_ technically true on git terms - on branch B, that file is
obviously dead).
Yann? Pavel? Anybody? Ideas?
Linus
^ 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