Skip to content

Commit a8dd2e7

Browse files
jherlandgitster
authored andcommitted
fast-import: Add support for importing commit notes
Introduce a 'notemodify' subcommand of the 'commit' command. This subcommand is similar to 'filemodify', except that no mode is supplied (all notes have mode 0644), and the path is set to the hex SHA1 of the given "comittish". This enables fast import of note objects along with their associated commits, since the notes can now be named using the mark references of their corresponding commits. The patch also includes a test case of the added functionality. Signed-off-by: Johan Herland <[email protected]> Acked-by: Shawn O. Pearce <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d9246d4 commit a8dd2e7

File tree

3 files changed

+289
-10
lines changed

3 files changed

+289
-10
lines changed

Documentation/git-fast-import.txt

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ change to the project.
316316
data
317317
('from' SP <committish> LF)?
318318
('merge' SP <committish> LF)?
319-
(filemodify | filedelete | filecopy | filerename | filedeleteall)*
319+
(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
320320
LF?
321321
....
322322

@@ -339,14 +339,13 @@ commit message use a 0 length data. Commit messages are free-form
339339
and are not interpreted by Git. Currently they must be encoded in
340340
UTF-8, as fast-import does not permit other encodings to be specified.
341341

342-
Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`
343-
and `filedeleteall` commands
342+
Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`,
343+
`filedeleteall` and `notemodify` commands
344344
may be included to update the contents of the branch prior to
345345
creating the commit. These commands may be supplied in any order.
346346
However it is recommended that a `filedeleteall` command precede
347-
all `filemodify`, `filecopy` and `filerename` commands in the same
348-
commit, as `filedeleteall`
349-
wipes the branch clean (see below).
347+
all `filemodify`, `filecopy`, `filerename` and `notemodify` commands in
348+
the same commit, as `filedeleteall` wipes the branch clean (see below).
350349

351350
The `LF` after the command is optional (it used to be required).
352351

@@ -595,6 +594,40 @@ more memory per active branch (less than 1 MiB for even most large
595594
projects); so frontends that can easily obtain only the affected
596595
paths for a commit are encouraged to do so.
597596

597+
`notemodify`
598+
^^^^^^^^^^^^
599+
Included in a `commit` command to add a new note (annotating a given
600+
commit) or change the content of an existing note. This command has
601+
two different means of specifying the content of the note.
602+
603+
External data format::
604+
The data content for the note was already supplied by a prior
605+
`blob` command. The frontend just needs to connect it to the
606+
commit that is to be annotated.
607+
+
608+
....
609+
'N' SP <dataref> SP <committish> LF
610+
....
611+
+
612+
Here `<dataref>` can be either a mark reference (`:<idnum>`)
613+
set by a prior `blob` command, or a full 40-byte SHA-1 of an
614+
existing Git blob object.
615+
616+
Inline data format::
617+
The data content for the note has not been supplied yet.
618+
The frontend wants to supply it as part of this modify
619+
command.
620+
+
621+
....
622+
'N' SP 'inline' SP <committish> LF
623+
data
624+
....
625+
+
626+
See below for a detailed description of the `data` command.
627+
628+
In both formats `<committish>` is any of the commit specification
629+
expressions also accepted by `from` (see above).
630+
598631
`mark`
599632
~~~~~~
600633
Arranges for fast-import to save a reference to the current object, allowing

fast-import.c

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ Format of STDIN stream:
2222
('author' sp name sp '<' email '>' sp when lf)?
2323
'committer' sp name sp '<' email '>' sp when lf
2424
commit_msg
25-
('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
26-
('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
25+
('from' sp committish lf)?
26+
('merge' sp committish lf)*
2727
file_change*
2828
lf?;
2929
commit_msg ::= data;
@@ -41,15 +41,18 @@ Format of STDIN stream:
4141
file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
4242
file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
4343
data;
44+
note_obm ::= 'N' sp (hexsha1 | idnum) sp committish lf;
45+
note_inm ::= 'N' sp 'inline' sp committish lf
46+
data;
4447
4548
new_tag ::= 'tag' sp tag_str lf
46-
'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
49+
'from' sp committish lf
4750
('tagger' sp name sp '<' email '>' sp when lf)?
4851
tag_msg;
4952
tag_msg ::= data;
5053
5154
reset_branch ::= 'reset' sp ref_str lf
52-
('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
55+
('from' sp committish lf)?
5356
lf?;
5457
5558
checkpoint ::= 'checkpoint' lf
@@ -88,6 +91,7 @@ Format of STDIN stream:
8891
# stream formatting is: \, " and LF. Otherwise these values
8992
# are UTF8.
9093
#
94+
committish ::= (ref_str | hexsha1 | sha1exp_str | idnum);
9195
ref_str ::= ref;
9296
sha1exp_str ::= sha1exp;
9397
tag_str ::= tag;
@@ -2006,6 +2010,80 @@ static void file_change_cr(struct branch *b, int rename)
20062010
leaf.tree);
20072011
}
20082012

2013+
static void note_change_n(struct branch *b)
2014+
{
2015+
const char *p = command_buf.buf + 2;
2016+
static struct strbuf uq = STRBUF_INIT;
2017+
struct object_entry *oe = oe;
2018+
struct branch *s;
2019+
unsigned char sha1[20], commit_sha1[20];
2020+
uint16_t inline_data = 0;
2021+
2022+
/* <dataref> or 'inline' */
2023+
if (*p == ':') {
2024+
char *x;
2025+
oe = find_mark(strtoumax(p + 1, &x, 10));
2026+
hashcpy(sha1, oe->sha1);
2027+
p = x;
2028+
} else if (!prefixcmp(p, "inline")) {
2029+
inline_data = 1;
2030+
p += 6;
2031+
} else {
2032+
if (get_sha1_hex(p, sha1))
2033+
die("Invalid SHA1: %s", command_buf.buf);
2034+
oe = find_object(sha1);
2035+
p += 40;
2036+
}
2037+
if (*p++ != ' ')
2038+
die("Missing space after SHA1: %s", command_buf.buf);
2039+
2040+
/* <committish> */
2041+
s = lookup_branch(p);
2042+
if (s) {
2043+
hashcpy(commit_sha1, s->sha1);
2044+
} else if (*p == ':') {
2045+
uintmax_t commit_mark = strtoumax(p + 1, NULL, 10);
2046+
struct object_entry *commit_oe = find_mark(commit_mark);
2047+
if (commit_oe->type != OBJ_COMMIT)
2048+
die("Mark :%" PRIuMAX " not a commit", commit_mark);
2049+
hashcpy(commit_sha1, commit_oe->sha1);
2050+
} else if (!get_sha1(p, commit_sha1)) {
2051+
unsigned long size;
2052+
char *buf = read_object_with_reference(commit_sha1,
2053+
commit_type, &size, commit_sha1);
2054+
if (!buf || size < 46)
2055+
die("Not a valid commit: %s", p);
2056+
free(buf);
2057+
} else
2058+
die("Invalid ref name or SHA1 expression: %s", p);
2059+
2060+
if (inline_data) {
2061+
static struct strbuf buf = STRBUF_INIT;
2062+
2063+
if (p != uq.buf) {
2064+
strbuf_addstr(&uq, p);
2065+
p = uq.buf;
2066+
}
2067+
read_next_command();
2068+
parse_data(&buf);
2069+
store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
2070+
} else if (oe) {
2071+
if (oe->type != OBJ_BLOB)
2072+
die("Not a blob (actually a %s): %s",
2073+
typename(oe->type), command_buf.buf);
2074+
} else {
2075+
enum object_type type = sha1_object_info(sha1, NULL);
2076+
if (type < 0)
2077+
die("Blob not found: %s", command_buf.buf);
2078+
if (type != OBJ_BLOB)
2079+
die("Not a blob (actually a %s): %s",
2080+
typename(type), command_buf.buf);
2081+
}
2082+
2083+
tree_content_set(&b->branch_tree, sha1_to_hex(commit_sha1), sha1,
2084+
S_IFREG | 0644, NULL);
2085+
}
2086+
20092087
static void file_change_deleteall(struct branch *b)
20102088
{
20112089
release_tree_content_recursive(b->branch_tree.tree);
@@ -2175,6 +2253,8 @@ static void parse_new_commit(void)
21752253
file_change_cr(b, 1);
21762254
else if (!prefixcmp(command_buf.buf, "C "))
21772255
file_change_cr(b, 0);
2256+
else if (!prefixcmp(command_buf.buf, "N "))
2257+
note_change_n(b);
21782258
else if (!strcmp("deleteall", command_buf.buf))
21792259
file_change_deleteall(b);
21802260
else {

t/t9300-fast-import.sh

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,4 +1088,170 @@ INPUT_END
10881088
test_expect_success 'P: fail on blob mark in gitlink' '
10891089
test_must_fail git fast-import <input'
10901090

1091+
###
1092+
### series Q (notes)
1093+
###
1094+
1095+
note1_data="Note for the first commit"
1096+
note2_data="Note for the second commit"
1097+
note3_data="Note for the third commit"
1098+
1099+
test_tick
1100+
cat >input <<INPUT_END
1101+
blob
1102+
mark :2
1103+
data <<EOF
1104+
$file2_data
1105+
EOF
1106+
1107+
commit refs/heads/notes-test
1108+
mark :3
1109+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1110+
data <<COMMIT
1111+
first (:3)
1112+
COMMIT
1113+
1114+
M 644 :2 file2
1115+
1116+
blob
1117+
mark :4
1118+
data $file4_len
1119+
$file4_data
1120+
commit refs/heads/notes-test
1121+
mark :5
1122+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1123+
data <<COMMIT
1124+
second (:5)
1125+
COMMIT
1126+
1127+
M 644 :4 file4
1128+
1129+
commit refs/heads/notes-test
1130+
mark :6
1131+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1132+
data <<COMMIT
1133+
third (:6)
1134+
COMMIT
1135+
1136+
M 644 inline file5
1137+
data <<EOF
1138+
$file5_data
1139+
EOF
1140+
1141+
M 755 inline file6
1142+
data <<EOF
1143+
$file6_data
1144+
EOF
1145+
1146+
blob
1147+
mark :7
1148+
data <<EOF
1149+
$note1_data
1150+
EOF
1151+
1152+
blob
1153+
mark :8
1154+
data <<EOF
1155+
$note2_data
1156+
EOF
1157+
1158+
commit refs/notes/foobar
1159+
mark :9
1160+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1161+
data <<COMMIT
1162+
notes (:9)
1163+
COMMIT
1164+
1165+
N :7 :3
1166+
N :8 :5
1167+
N inline :6
1168+
data <<EOF
1169+
$note3_data
1170+
EOF
1171+
1172+
INPUT_END
1173+
test_expect_success \
1174+
'Q: commit notes' \
1175+
'git fast-import <input &&
1176+
git whatchanged notes-test'
1177+
test_expect_success \
1178+
'Q: verify pack' \
1179+
'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
1180+
1181+
commit1=$(git rev-parse notes-test~2)
1182+
commit2=$(git rev-parse notes-test^)
1183+
commit3=$(git rev-parse notes-test)
1184+
1185+
cat >expect <<EOF
1186+
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1187+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1188+
1189+
first (:3)
1190+
EOF
1191+
test_expect_success \
1192+
'Q: verify first commit' \
1193+
'git cat-file commit notes-test~2 | sed 1d >actual &&
1194+
test_cmp expect actual'
1195+
1196+
cat >expect <<EOF
1197+
parent $commit1
1198+
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1199+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1200+
1201+
second (:5)
1202+
EOF
1203+
test_expect_success \
1204+
'Q: verify second commit' \
1205+
'git cat-file commit notes-test^ | sed 1d >actual &&
1206+
test_cmp expect actual'
1207+
1208+
cat >expect <<EOF
1209+
parent $commit2
1210+
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1211+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1212+
1213+
third (:6)
1214+
EOF
1215+
test_expect_success \
1216+
'Q: verify third commit' \
1217+
'git cat-file commit notes-test | sed 1d >actual &&
1218+
test_cmp expect actual'
1219+
1220+
cat >expect <<EOF
1221+
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1222+
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1223+
1224+
notes (:9)
1225+
EOF
1226+
test_expect_success \
1227+
'Q: verify notes commit' \
1228+
'git cat-file commit refs/notes/foobar | sed 1d >actual &&
1229+
test_cmp expect actual'
1230+
1231+
cat >expect.unsorted <<EOF
1232+
100644 blob $commit1
1233+
100644 blob $commit2
1234+
100644 blob $commit3
1235+
EOF
1236+
cat expect.unsorted | sort >expect
1237+
test_expect_success \
1238+
'Q: verify notes tree' \
1239+
'git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
1240+
test_cmp expect actual'
1241+
1242+
echo "$note1_data" >expect
1243+
test_expect_success \
1244+
'Q: verify note for first commit' \
1245+
'git cat-file blob refs/notes/foobar:$commit1 >actual && test_cmp expect actual'
1246+
1247+
echo "$note2_data" >expect
1248+
test_expect_success \
1249+
'Q: verify note for second commit' \
1250+
'git cat-file blob refs/notes/foobar:$commit2 >actual && test_cmp expect actual'
1251+
1252+
echo "$note3_data" >expect
1253+
test_expect_success \
1254+
'Q: verify note for third commit' \
1255+
'git cat-file blob refs/notes/foobar:$commit3 >actual && test_cmp expect actual'
1256+
10911257
test_done

0 commit comments

Comments
 (0)