Skip to content

Commit

Permalink
Merge branch 'svn-fe' of git://repo.or.cz/git/jrn
Browse files Browse the repository at this point in the history
* 'svn-fe' of git://repo.or.cz/git/jrn: (31 commits)
  fast-import: make code "-Wpointer-arith" clean
  vcs-svn: teach line_buffer about temporary files
  vcs-svn: allow input from file descriptor
  vcs-svn: allow character-oriented input
  vcs-svn: add binary-safe read function
  t0081 (line-buffer): add buffering tests
  vcs-svn: tweak test-line-buffer to not assume line-oriented input
  tests: give vcs-svn/line_buffer its own test script
  vcs-svn: make test-line-buffer input format more flexible
  vcs-svn: teach line_buffer to handle multiple input files
  vcs-svn: collect line_buffer data in a struct
  vcs-svn: replace buffer_read_string memory pool with a strbuf
  vcs-svn: eliminate global byte_buffer
  fast-import: add 'ls' command
  vcs-svn: Allow change nodes for root of tree (/)
  vcs-svn: Implement Prop-delta handling
  vcs-svn: Sharpen parsing of property lines
  vcs-svn: Split off function for handling of individual properties
  vcs-svn: Make source easier to read on small screens
  vcs-svn: More dump format sanity checks
  ...
  • Loading branch information
Junio C Hamano committed Mar 1, 2011
2 parents afb0b79 + 6288e3e commit f70f736
Show file tree
Hide file tree
Showing 18 changed files with 1,571 additions and 233 deletions.
63 changes: 60 additions & 3 deletions Documentation/git-fast-import.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ especially when a higher level language such as Perl, Python or
Ruby is being used.

fast-import is very strict about its input. Where we say SP below we mean
*exactly* one space. Likewise LF means one (and only one) linefeed.
*exactly* one space. Likewise LF means one (and only one) linefeed
and HT one (and only one) horizontal tab.
Supplying additional whitespace characters will cause unexpected
results, such as branch names or file names with leading or trailing
spaces in their name, or early termination of fast-import when it encounters
Expand Down Expand Up @@ -334,6 +335,11 @@ and control the current import process. More detailed discussion
format to the file descriptor set with `--cat-blob-fd` or
`stdout` if unspecified.

`ls`::
Causes fast-import to print a line describing a directory
entry in 'ls-tree' format to the file descriptor set with
`--cat-blob-fd` or `stdout` if unspecified.

`feature`::
Require that fast-import supports the specified feature, or
abort if it does not.
Expand Down Expand Up @@ -919,6 +925,55 @@ This command can be used anywhere in the stream that comments are
accepted. In particular, the `cat-blob` command can be used in the
middle of a commit but not in the middle of a `data` command.

`ls`
~~~~
Prints information about the object at a path to a file descriptor
previously arranged with the `--cat-blob-fd` argument. This allows
printing a blob from the active commit (with `cat-blob`) or copying a
blob or tree from a previous commit for use in the current one (with
`filemodify`).

The `ls` command can be used anywhere in the stream that comments are
accepted, including the middle of a commit.

Reading from the active commit::
This form can only be used in the middle of a `commit`.
The path names a directory entry within fast-import's
active commit. The path must be quoted in this case.
+
....
'ls' SP <path> LF
....

Reading from a named tree::
The `<dataref>` can be a mark reference (`:<idnum>`) or the
full 40-byte SHA-1 of a Git tag, commit, or tree object,
preexisting or waiting to be written.
The path is relative to the top level of the tree
named by `<dataref>`.
+
....
'ls' SP <dataref> SP <path> LF
....

See `filemodify` above for a detailed description of `<path>`.

Output uses the same format as `git ls-tree <tree> {litdd} <path>`:

====
<mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF
====

The <dataref> represents the blob, tree, or commit object at <path>
and can be used in later 'cat-blob', 'filemodify', or 'ls' commands.

If there is no file or subtree at that path, 'git fast-import' will
instead report

====
missing SP <path> LF
====

`feature`
~~~~~~~~~
Require that fast-import supports the specified feature, or abort if
Expand Down Expand Up @@ -946,8 +1001,10 @@ import-marks::
any "feature import-marks" command in the stream.

cat-blob::
Ignored. Versions of fast-import not supporting the
"cat-blob" command will exit with a message indicating so.
ls::
Require that the backend support the 'cat-blob' or 'ls' command.
Versions of fast-import not supporting the specified command
will exit with a message indicating so.
This lets the import error out early with a clear message,
rather than wasting time on the early part of an import
before the unsupported command is detected.
Expand Down
3 changes: 2 additions & 1 deletion contrib/svn-fe/svn-fe.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

int main(int argc, char **argv)
{
svndump_init(NULL);
if (svndump_init(NULL))
return 1;
svndump_read((argc > 1) ? argv[1] : NULL);
svndump_deinit();
svndump_reset();
Expand Down
162 changes: 159 additions & 3 deletions fast-import.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ Format of STDIN stream:
commit_msg
('from' sp committish lf)?
('merge' sp committish lf)*
file_change*
(file_change | ls)*
lf?;
commit_msg ::= data;
ls ::= 'ls' sp '"' quoted(path) '"' lf;
file_change ::= file_clr
| file_del
| file_rnm
Expand Down Expand Up @@ -132,7 +134,7 @@ Format of STDIN stream:
ts ::= # time since the epoch in seconds, ascii base10 notation;
tz ::= # GIT style timezone;
# note: comments and cat requests may appear anywhere
# note: comments, ls and cat requests may appear anywhere
# in the input, except within a data command. Any form
# of the data command always escapes the related input
# from comment processing.
Expand All @@ -141,7 +143,9 @@ Format of STDIN stream:
# must be the first character on that line (an lf
# preceded it).
#
cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
ls_tree ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
comment ::= '#' not_lf* lf;
not_lf ::= # Any byte that is not ASCII newline (LF);
Expand Down Expand Up @@ -374,6 +378,7 @@ static int cat_blob_fd = STDOUT_FILENO;

static void parse_argv(void);
static void parse_cat_blob(void);
static void parse_ls(struct branch *b);

static void write_branch_report(FILE *rpt, struct branch *b)
{
Expand Down Expand Up @@ -2614,6 +2619,8 @@ static void parse_new_commit(void)
note_change_n(b, prev_fanout);
else if (!strcmp("deleteall", command_buf.buf))
file_change_deleteall(b);
else if (!prefixcmp(command_buf.buf, "ls "))
parse_ls(b);
else {
unread_command_buf = 1;
break;
Expand Down Expand Up @@ -2837,6 +2844,153 @@ static void parse_cat_blob(void)
cat_blob(oe, sha1);
}

static struct object_entry *dereference(struct object_entry *oe,
unsigned char sha1[20])
{
unsigned long size;
char *buf = NULL;
if (!oe) {
enum object_type type = sha1_object_info(sha1, NULL);
if (type < 0)
die("object not found: %s", sha1_to_hex(sha1));
/* cache it! */
oe = insert_object(sha1);
oe->type = type;
oe->pack_id = MAX_PACK_ID;
oe->idx.offset = 1;
}
switch (oe->type) {
case OBJ_TREE: /* easy case. */
return oe;
case OBJ_COMMIT:
case OBJ_TAG:
break;
default:
die("Not a treeish: %s", command_buf.buf);
}

if (oe->pack_id != MAX_PACK_ID) { /* in a pack being written */
buf = gfi_unpack_entry(oe, &size);
} else {
enum object_type unused;
buf = read_sha1_file(sha1, &unused, &size);
}
if (!buf)
die("Can't load object %s", sha1_to_hex(sha1));

/* Peel one layer. */
switch (oe->type) {
case OBJ_TAG:
if (size < 40 + strlen("object ") ||
get_sha1_hex(buf + strlen("object "), sha1))
die("Invalid SHA1 in tag: %s", command_buf.buf);
break;
case OBJ_COMMIT:
if (size < 40 + strlen("tree ") ||
get_sha1_hex(buf + strlen("tree "), sha1))
die("Invalid SHA1 in commit: %s", command_buf.buf);
}

free(buf);
return find_object(sha1);
}

static struct object_entry *parse_treeish_dataref(const char **p)
{
unsigned char sha1[20];
struct object_entry *e;

if (**p == ':') { /* <mark> */
char *endptr;
e = find_mark(strtoumax(*p + 1, &endptr, 10));
if (endptr == *p + 1)
die("Invalid mark: %s", command_buf.buf);
if (!e)
die("Unknown mark: %s", command_buf.buf);
*p = endptr;
hashcpy(sha1, e->idx.sha1);
} else { /* <sha1> */
if (get_sha1_hex(*p, sha1))
die("Invalid SHA1: %s", command_buf.buf);
e = find_object(sha1);
*p += 40;
}

while (!e || e->type != OBJ_TREE)
e = dereference(e, sha1);
return e;
}

static void print_ls(int mode, const unsigned char *sha1, const char *path)
{
static struct strbuf line = STRBUF_INIT;

/* See show_tree(). */
const char *type =
S_ISGITLINK(mode) ? commit_type :
S_ISDIR(mode) ? tree_type :
blob_type;

if (!mode) {
/* missing SP path LF */
strbuf_reset(&line);
strbuf_addstr(&line, "missing ");
quote_c_style(path, &line, NULL, 0);
strbuf_addch(&line, '\n');
} else {
/* mode SP type SP object_name TAB path LF */
strbuf_reset(&line);
strbuf_addf(&line, "%06o %s %s\t",
mode, type, sha1_to_hex(sha1));
quote_c_style(path, &line, NULL, 0);
strbuf_addch(&line, '\n');
}
cat_blob_write(line.buf, line.len);
}

static void parse_ls(struct branch *b)
{
const char *p;
struct tree_entry *root = NULL;
struct tree_entry leaf = {0};

/* ls SP (<treeish> SP)? <path> */
p = command_buf.buf + strlen("ls ");
if (*p == '"') {
if (!b)
die("Not in a commit: %s", command_buf.buf);
root = &b->branch_tree;
} else {
struct object_entry *e = parse_treeish_dataref(&p);
root = new_tree_entry();
hashcpy(root->versions[1].sha1, e->idx.sha1);
load_tree(root);
if (*p++ != ' ')
die("Missing space after tree-ish: %s", command_buf.buf);
}
if (*p == '"') {
static struct strbuf uq = STRBUF_INIT;
const char *endp;
strbuf_reset(&uq);
if (unquote_c_style(&uq, p, &endp))
die("Invalid path: %s", command_buf.buf);
if (*endp)
die("Garbage after path in: %s", command_buf.buf);
p = uq.buf;
}
tree_content_get(root, p, &leaf);
/*
* A directory in preparation would have a sha1 of zero
* until it is saved. Save, for simplicity.
*/
if (S_ISDIR(leaf.versions[1].mode))
store_tree(&leaf);

print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
if (!b || root != &b->branch_tree)
release_tree_entry(root);
}

static void checkpoint(void)
{
checkpoint_requested = 0;
Expand Down Expand Up @@ -3001,7 +3155,7 @@ static int parse_one_feature(const char *feature, int from_stream)
relative_marks_paths = 0;
} else if (!prefixcmp(feature, "force")) {
force_update = 1;
} else if (!strcmp(feature, "notes")) {
} else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
; /* do nothing; we have the feature */
} else {
return 0;
Expand Down Expand Up @@ -3142,6 +3296,8 @@ int main(int argc, const char **argv)
while (read_next_command() != EOF) {
if (!strcmp("blob", command_buf.buf))
parse_new_blob();
else if (!prefixcmp(command_buf.buf, "ls "))
parse_ls(NULL);
else if (!prefixcmp(command_buf.buf, "commit "))
parse_new_commit();
else if (!prefixcmp(command_buf.buf, "tag "))
Expand Down
54 changes: 0 additions & 54 deletions t/t0080-vcs-svn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,60 +76,6 @@ test_expect_success 'obj pool: high-water mark' '
test_cmp expected actual
'

test_expect_success 'line buffer' '
echo HELLO >expected1 &&
printf "%s\n" "" HELLO >expected2 &&
echo >expected3 &&
printf "%s\n" "" Q | q_to_nul >expected4 &&
printf "%s\n" foo "" >expected5 &&
printf "%s\n" "" foo >expected6 &&
test-line-buffer <<-\EOF >actual1 &&
5
HELLO
EOF
test-line-buffer <<-\EOF >actual2 &&
0
5
HELLO
EOF
q_to_nul <<-\EOF |
1
Q
EOF
test-line-buffer >actual3 &&
q_to_nul <<-\EOF |
0
1
Q
EOF
test-line-buffer >actual4 &&
test-line-buffer <<-\EOF >actual5 &&
5
foo
EOF
test-line-buffer <<-\EOF >actual6 &&
0
5
foo
EOF
test_cmp expected1 actual1 &&
test_cmp expected2 actual2 &&
test_cmp expected3 actual3 &&
test_cmp expected4 actual4 &&
test_cmp expected5 actual5 &&
test_cmp expected6 actual6
'

test_expect_success 'string pool' '
echo a does not equal b >expected.differ &&
echo a equals a >expected.match &&
Expand Down
Loading

0 comments on commit f70f736

Please sign in to comment.