Teach fast-import how to clear the internal branch content.
authorShawn O. Pearce <spearce@spearce.org>
Wed, 7 Feb 2007 07:03:03 +0000 (02:03 -0500)
committerShawn O. Pearce <spearce@spearce.org>
Wed, 7 Feb 2007 07:03:03 +0000 (02:03 -0500)
Some frontends may not be able to (easily) keep track of which files
are included in the branch, and which aren't.  Performing this
tracking can be tedious and error prone for the frontend to do,
especially if its foreign data source cannot supply the changed
path list on a per-commit basis.

fast-import now allows a frontend to request that a branch's tree
be wiped clean (reset to the empty tree) at the start of a commit,
allowing the frontend to feed in all paths which belong on the branch.

This is ideal for a tar-file importer frontend, for example, as
the frontend just needs to reformat the tar data stream into a gfi
data stream, which may be something a few Perl regexps can take
care of. :)

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Documentation/git-fast-import.txt
fast-import.c
t/t9300-fast-import.sh

index aeddd5e..8bb5a0b 100644 (file)
@@ -269,7 +269,7 @@ change to the project.
        data
        ('from' SP <committish> LF)?
        ('merge' SP <committish> LF)?
-       (filemodify | filedelete)*
+       (filemodify | filedelete | filedeleteall)*
        LF
 ....
 
@@ -292,10 +292,12 @@ 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 gfi does not permit other encodings to be specified.
 
-Zero or more `filemodify` and `filedelete` commands may be
-included to update the contents of the branch prior to the commit.
-These commands can be supplied in any order, gfi is not sensitive
-to pathname or operation ordering.
+Zero or more `filemodify`, `filedelete` 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` commands in the same commit, as `filedeleteall`
+wipes the branch clean (see below).
 
 `author`
 ^^^^^^^^
@@ -459,6 +461,30 @@ first non-empty directory or the root is reached.
 here `<path>` is the complete path of the file to be removed.
 See `filemodify` above for a detailed description of `<path>`.
 
+`filedeleteall`
+^^^^^^^^^^^^^^^
+Included in a `commit` command to remove all files (and also all
+directories) from the branch.  This command resets the internal
+branch structure to have no files in it, allowing the frontend
+to subsequently add all interesting files from scratch.
+
+....
+       'deleteall' LF
+....
+
+This command is extremely useful if the frontend does not know
+(or does not care to know) what files are currently on the branch,
+and therefore cannot generate the proper `filedelete` commands to
+update the content.
+
+Issuing a `filedeleteall` followed by the needed `filemodify`
+commands to set the correct content will produce the same results
+as sending only the needed `filemodify` and `filedelete` commands.
+The `filedeleteall` approach may however require gfi to use slightly
+more memory per active branch (less than 1 MiB for even most large
+projects); so frontends that can easily obtain only the affected
+paths for a commit are encouraged to do so.
+
 `mark`
 ~~~~~~
 Arranges for gfi to save a reference to the current object, allowing
index c72c5c7..f80ddee 100644 (file)
@@ -26,7 +26,8 @@ Format of STDIN stream:
     lf;
   commit_msg ::= data;
 
-  file_change ::= file_del | file_obm | file_inm;
+  file_change ::= file_clr | file_del | file_obm | file_inm;
+  file_clr ::= 'deleteall' lf;
   file_del ::= 'D' 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
@@ -1640,6 +1641,14 @@ static void file_change_d(struct branch *b)
        free(p_uq);
 }
 
+static void file_change_deleteall(struct branch *b)
+{
+       release_tree_content_recursive(b->branch_tree.tree);
+       hashclr(b->branch_tree.versions[0].sha1);
+       hashclr(b->branch_tree.versions[1].sha1);
+       load_tree(&b->branch_tree);
+}
+
 static void cmd_from(struct branch *b)
 {
        const char *from;
@@ -1784,6 +1793,8 @@ static void cmd_new_commit(void)
                        file_change_m(b);
                else if (!strncmp("D ", command_buf.buf, 2))
                        file_change_d(b);
+               else if (!strcmp("deleteall", command_buf.buf))
+                       file_change_deleteall(b);
                else
                        die("Unsupported file_change: %s", command_buf.buf);
                read_next_command();
index 23a2ba7..357a872 100755 (executable)
@@ -356,4 +356,55 @@ test_expect_success \
        'test $old_branch != `git-rev-parse --verify branch^0` &&
         test $old_branch = `git-rev-parse --verify branch@{1}`'
 
+###
+### series H
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/H
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third
+COMMIT
+
+from refs/heads/branch^0
+M 644 inline i-will-die
+data <<EOF
+this file will never exist.
+EOF
+
+deleteall
+M 644 inline h/e/l/lo
+data <<EOF
+$file5_data
+EOF
+
+INPUT_END
+test_expect_success \
+    'H: deletall, add 1' \
+    'git-fast-import <input &&
+        git-whatchanged H'
+test_expect_success \
+       'H: verify pack' \
+       'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+
+cat >expect <<EOF
+:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D     file2/newf
+:100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D     file2/oldf
+:100755 000000 85df50785d62d3b05ab03d9cbf7e4a0b49449730 0000000000000000000000000000000000000000 D     file4
+:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100  newdir/interesting      h/e/l/lo
+:100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D     newdir/exec.sh
+EOF
+git-diff-tree -M -r H^ H >actual
+test_expect_success \
+       'H: validate old files removed, new files added' \
+       'compare_diff_raw expect actual'
+
+echo "$file5_data" >expect
+test_expect_success \
+       'H: verify file' \
+       'git-cat-file blob H:h/e/l/lo >actual &&
+        diff -u expect actual'
+
 test_done