diff options
author | Junio C Hamano <gitster@pobox.com> | 2007-07-14 22:57:47 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2007-07-14 22:57:47 -0700 |
commit | 3f2fd36ebcc2ef15df196bd0544cc9c39da57dbf (patch) | |
tree | 3e94886b0818762f8f2eeb047c5f59801d93fa23 | |
parent | a82830a4577dfe93d329918e7624741fee13842c (diff) | |
parent | b6f3481bb456acbbb990a1045344bb06e5a40283 (diff) | |
download | git-3f2fd36ebcc2ef15df196bd0544cc9c39da57dbf.tar.gz |
Merge branch 'master' of git://repo.or.cz/git/fastimport
* 'master' of git://repo.or.cz/git/fastimport:
Teach fast-import to recursively copy files/directories
Fix git-p4 on Windows to not use the Posix sysconf function.
Correct trivial typo in fast-import documentation
-rw-r--r-- | Documentation/git-fast-import.txt | 40 | ||||
-rwxr-xr-x | contrib/fast-import/git-p4 | 6 | ||||
-rw-r--r-- | fast-import.c | 81 | ||||
-rwxr-xr-x | t/t9300-fast-import.sh | 83 |
4 files changed, 200 insertions, 10 deletions
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 80a8ee0f1c..30ee98d17f 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -302,7 +302,7 @@ change to the project. data ('from' SP <committish> LF)? ('merge' SP <committish> LF)? - (filemodify | filedelete | filerename | filedeleteall)* + (filemodify | filedelete | filecopy | filerename | filedeleteall)* LF .... @@ -325,13 +325,13 @@ commit message use a 0 length data. Commit messages are free-form and are not interpreted by Git. Currently they must be encoded in UTF-8, as fast-import does not permit other encodings to be specified. -Zero or more `filemodify`, `filedelete`, `filename` and -`filedeleteall` commands +Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename` +and `filedeleteall` commands may be included to update the contents of the branch prior to creating the commit. These commands may be supplied in any order. However it is recommended that a `filedeleteall` command preceed -all `filemodify` and `filerename` commands in the same commit, as -`filedeleteall` +all `filemodify`, `filecopy` and `filerename` commands in the same +commit, as `filedeleteall` wipes the branch clean (see below). `author` @@ -497,6 +497,27 @@ here `<path>` is the complete path of the file or subdirectory to be removed from the branch. See `filemodify` above for a detailed description of `<path>`. +`filecopy` +^^^^^^^^^^^^ +Recursively copies an existing file or subdirectory to a different +location within the branch. The existing file or directory must +exist. If the destination exists it will be completely replaced +by the content copied from the source. + +.... + 'C' SP <path> SP <path> LF +.... + +here the first `<path>` is the source location and the second +`<path>` is the destination. See `filemodify` above for a detailed +description of what `<path>` may look like. To use a source path +that contains SP the path must be quoted. + +A `filecopy` command takes effect immediately. Once the source +location has been copied to the destination any future commands +applied to the source location will not impact the destination of +the copy. + `filerename` ^^^^^^^^^^^^ Renames an existing file or subdirectory to a different location @@ -517,6 +538,15 @@ location has been renamed to the destination any future commands applied to the source location will create new files there and not impact the destination of the rename. +Note that a `filerename` is the same as a `filecopy` followed by a +`filedelete` of the source location. There is a slight performance +advantage to using `filerename`, but the advantage is so small +that it is never worth trying to convert a delete/add pair in +source material into a rename for fast-import. This `filerename` +command is provided just to simplify frontends that already have +rename information and don't want bother with decomposing it into a +`filecopy` followed by a `filedelete`. + `filedeleteall` ^^^^^^^^^^^^^^^ Included in a `commit` command to remove all files (and also all diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 54a05eb99c..d877150f41 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -717,7 +717,11 @@ class P4Sync(Command): # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. # and all OS from the table below seems to be higher than POSIX. # See http://www.in-ulm.de/~mascheck/various/argmax/ - argmax = min(4000, os.sysconf('SC_ARG_MAX')) + if (self.isWindows): + argmax = 2000 + else: + argmax = min(4000, os.sysconf('SC_ARG_MAX')) + chunk = '' filedata = [] for i in xrange(len(files)): diff --git a/fast-import.c b/fast-import.c index a1cb13f09b..99a19d89e8 100644 --- a/fast-import.c +++ b/fast-import.c @@ -26,10 +26,16 @@ Format of STDIN stream: lf; commit_msg ::= data; - file_change ::= file_clr | file_del | file_rnm | file_obm | file_inm; + file_change ::= file_clr + | file_del + | file_rnm + | file_cpy + | file_obm + | file_inm; file_clr ::= 'deleteall' lf; file_del ::= 'D' sp path_str lf; file_rnm ::= 'R' sp path_str sp path_str lf; + file_cpy ::= 'C' sp path_str sp path_str lf; file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf; file_inm ::= 'M' sp mode sp 'inline' sp path_str lf data; @@ -623,6 +629,31 @@ static void release_tree_entry(struct tree_entry *e) avail_tree_entry = e; } +static struct tree_content *dup_tree_content(struct tree_content *s) +{ + struct tree_content *d; + struct tree_entry *a, *b; + unsigned int i; + + if (!s) + return NULL; + d = new_tree_content(s->entry_count); + for (i = 0; i < s->entry_count; i++) { + a = s->entries[i]; + b = new_tree_entry(); + memcpy(b, a, sizeof(*a)); + if (a->tree && is_null_sha1(b->versions[1].sha1)) + b->tree = dup_tree_content(a->tree); + else + b->tree = NULL; + d->entries[i] = b; + } + d->entry_count = s->entry_count; + d->delta_depth = s->delta_depth; + + return d; +} + static void start_packfile(void) { static char tmpfile[PATH_MAX]; @@ -1273,6 +1304,43 @@ del_entry: return 1; } +static int tree_content_get( + struct tree_entry *root, + const char *p, + struct tree_entry *leaf) +{ + struct tree_content *t = root->tree; + const char *slash1; + unsigned int i, n; + struct tree_entry *e; + + slash1 = strchr(p, '/'); + if (slash1) + n = slash1 - p; + else + n = strlen(p); + + for (i = 0; i < t->entry_count; i++) { + e = t->entries[i]; + if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) { + if (!slash1) { + memcpy(leaf, e, sizeof(*leaf)); + if (e->tree && is_null_sha1(e->versions[1].sha1)) + leaf->tree = dup_tree_content(e->tree); + else + leaf->tree = NULL; + return 1; + } + if (!S_ISDIR(e->versions[1].mode)) + return 0; + if (!e->tree) + load_tree(e); + return tree_content_get(e, slash1 + 1, leaf); + } + } + return 0; +} + static int update_branch(struct branch *b) { static const char *msg = "fast-import"; @@ -1658,7 +1726,7 @@ static void file_change_d(struct branch *b) free(p_uq); } -static void file_change_r(struct branch *b) +static void file_change_cr(struct branch *b, int rename) { const char *s, *d; char *s_uq, *d_uq; @@ -1694,7 +1762,10 @@ static void file_change_r(struct branch *b) } memset(&leaf, 0, sizeof(leaf)); - tree_content_remove(&b->branch_tree, s, &leaf); + if (rename) + tree_content_remove(&b->branch_tree, s, &leaf); + else + tree_content_get(&b->branch_tree, s, &leaf); if (!leaf.versions[1].mode) die("Path %s not in branch", s); tree_content_set(&b->branch_tree, d, @@ -1874,7 +1945,9 @@ static void cmd_new_commit(void) else if (!prefixcmp(command_buf.buf, "D ")) file_change_d(b); else if (!prefixcmp(command_buf.buf, "R ")) - file_change_r(b); + file_change_cr(b, 1); + else if (!prefixcmp(command_buf.buf, "C ")) + file_change_cr(b, 0); else if (!strcmp("deleteall", command_buf.buf)) file_change_deleteall(b); else diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index bf3720d762..4b920be331 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -648,4 +648,87 @@ test_expect_success \ git diff-tree -M -r M3^ M3 >actual && compare_diff_raw expect actual' +### +### series N +### + +test_tick +cat >input <<INPUT_END +commit refs/heads/N1 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +file copy +COMMIT + +from refs/heads/branch^0 +C file2/newf file2/n.e.w.f + +INPUT_END + +cat >expect <<EOF +:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file2/n.e.w.f +EOF +test_expect_success \ + 'N: copy file in same subdirectory' \ + 'git-fast-import <input && + git diff-tree -C --find-copies-harder -r N1^ N1 >actual && + compare_diff_raw expect actual' + +cat >input <<INPUT_END +commit refs/heads/N2 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +clean directory copy +COMMIT + +from refs/heads/branch^0 +C file2 file3 + +commit refs/heads/N2 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +modify directory copy +COMMIT + +M 644 inline file3/file5 +data <<EOF +$file5_data +EOF + +INPUT_END + +cat >expect <<EOF +:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5 +:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf +:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf +EOF +test_expect_success \ + 'N: copy then modify subdirectory' \ + 'git-fast-import <input && + git diff-tree -C --find-copies-harder -r N2^^ N2 >actual && + compare_diff_raw expect actual' + +cat >input <<INPUT_END +commit refs/heads/N3 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +dirty directory copy +COMMIT + +from refs/heads/branch^0 +M 644 inline file2/file5 +data <<EOF +$file5_data +EOF + +C file2 file3 +D file2/file5 + +INPUT_END + +test_expect_success \ + 'N: copy dirty subdirectory' \ + 'git-fast-import <input && + test `git-rev-parse N2^{tree}` = `git-rev-parse N3^{tree}`' + test_done |