24
commit_msg
25
('from' sp committish lf)?
26
('merge' sp committish lf)*
27
- file_change*
27
+ (file_change | ls)*
28
lf?;
29
commit_msg ::= data;
30
31
+ ls ::= 'ls' sp '"' quoted(path) '"' lf;
32
+
33
file_change ::= file_clr
34
| file_del
35
| file_rnm
134
ts ::= # time since the epoch in seconds, ascii base10 notation;
135
tz ::= # GIT style timezone;
136
135
- # note: comments and cat requests may appear anywhere
137
+ # note: comments, ls and cat requests may appear anywhere
138
# in the input, except within a data command. Any form
139
# of the data command always escapes the related input
140
# from comment processing.
143
# must be the first character on that line (an lf
144
# preceded it).
145
#
146
+
147
cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
148
+ ls_tree ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
149
150
comment ::= '#' not_lf* lf;
151
not_lf ::= # Any byte that is not ASCII newline (LF);
378
379
static void parse_argv(void);
380
static void parse_cat_blob(void);
381
+static void parse_ls(struct branch *b);
382
383
static void write_branch_report(FILE *rpt, struct branch *b)
384
{
2619
note_change_n(b, prev_fanout);
2620
else if (!strcmp("deleteall", command_buf.buf))
2621
file_change_deleteall(b);
2622
+ else if (!prefixcmp(command_buf.buf, "ls "))
2623
+ parse_ls(b);
2624
else {
2625
unread_command_buf = 1;
2626
break;
2844
cat_blob(oe, sha1);
2845
}
2846
2847
+static struct object_entry *dereference(struct object_entry *oe,
2848
+ unsigned char sha1[20])
2849
+{
2850
+ unsigned long size;
2851
+ void *buf = NULL;
2852
+ if (!oe) {
2853
+ enum object_type type = sha1_object_info(sha1, NULL);
2854
+ if (type < 0)
2855
+ die("object not found: %s", sha1_to_hex(sha1));
2856
+ /* cache it! */
2857
+ oe = insert_object(sha1);
2858
+ oe->type = type;
2859
+ oe->pack_id = MAX_PACK_ID;
2860
+ oe->idx.offset = 1;
2861
+ }
2862
+ switch (oe->type) {
2863
+ case OBJ_TREE: /* easy case. */
2864
+ return oe;
2865
+ case OBJ_COMMIT:
2866
+ case OBJ_TAG:
2867
+ break;
2868
+ default:
2869
+ die("Not a treeish: %s", command_buf.buf);
2870
+ }
2871
+
2872
+ if (oe->pack_id != MAX_PACK_ID) { /* in a pack being written */
2873
+ buf = gfi_unpack_entry(oe, &size);
2874
+ } else {
2875
+ enum object_type unused;
2876
+ buf = read_sha1_file(sha1, &unused, &size);
2877
+ }
2878
+ if (!buf)
2879
+ die("Can't load object %s", sha1_to_hex(sha1));
2880
+
2881
+ /* Peel one layer. */
2882
+ switch (oe->type) {
2883
+ case OBJ_TAG:
2884
+ if (size < 40 + strlen("object ") ||
2885
+ get_sha1_hex(buf + strlen("object "), sha1))
2886
+ die("Invalid SHA1 in tag: %s", command_buf.buf);
2887
+ break;
2888
+ case OBJ_COMMIT:
2889
+ if (size < 40 + strlen("tree ") ||
2890
+ get_sha1_hex(buf + strlen("tree "), sha1))
2891
+ die("Invalid SHA1 in commit: %s", command_buf.buf);
2892
+ }
2893
+
2894
+ free(buf);
2895
+ return find_object(sha1);
2896
+}
2897
+
2898
+static struct object_entry *parse_treeish_dataref(const char **p)
2899
+{
2900
+ unsigned char sha1[20];
2901
+ struct object_entry *e;
2902
+
2903
+ if (**p == ':') { /* <mark> */
2904
+ char *endptr;
2905
+ e = find_mark(strtoumax(*p + 1, &endptr, 10));
2906
+ if (endptr == *p + 1)
2907
+ die("Invalid mark: %s", command_buf.buf);
2908
+ if (!e)
2909
+ die("Unknown mark: %s", command_buf.buf);
2910
+ *p = endptr;
2911
+ hashcpy(sha1, e->idx.sha1);
2912
+ } else { /* <sha1> */
2913
+ if (get_sha1_hex(*p, sha1))
2914
+ die("Invalid SHA1: %s", command_buf.buf);
2915
+ e = find_object(sha1);
2916
+ *p += 40;
2917
+ }
2918
+
2919
+ while (!e || e->type != OBJ_TREE)
2920
+ e = dereference(e, sha1);
2921
+ return e;
2922
+}
2923
+
2924
+static void print_ls(int mode, const unsigned char *sha1, const char *path)
2925
+{
2926
+ static struct strbuf line = STRBUF_INIT;
2927
+
2928
+ /* See show_tree(). */
2929
+ const char *type =
2930
+ S_ISGITLINK(mode) ? commit_type :
2931
+ S_ISDIR(mode) ? tree_type :
2932
+ blob_type;
2933
+
2934
+ if (!mode) {
2935
+ /* missing SP path LF */
2936
+ strbuf_reset(&line);
2937
+ strbuf_addstr(&line, "missing ");
2938
+ quote_c_style(path, &line, NULL, 0);
2939
+ strbuf_addch(&line, '\n');
2940
+ } else {
2941
+ /* mode SP type SP object_name TAB path LF */
2942
+ strbuf_reset(&line);
2943
+ strbuf_addf(&line, "%06o %s %s\t",
2944
+ mode, type, sha1_to_hex(sha1));
2945
+ quote_c_style(path, &line, NULL, 0);
2946
+ strbuf_addch(&line, '\n');
2947
+ }
2948
+ cat_blob_write(line.buf, line.len);
2949
+}
2950
+
2951
+static void parse_ls(struct branch *b)
2952
+{
2953
+ const char *p;
2954
+ struct tree_entry *root = NULL;
2955
+ struct tree_entry leaf = {0};
2956
+
2957
+ /* ls SP (<treeish> SP)? <path> */
2958
+ p = command_buf.buf + strlen("ls ");
2959
+ if (*p == '"') {
2960
+ if (!b)
2961
+ die("Not in a commit: %s", command_buf.buf);
2962
+ root = &b->branch_tree;
2963
+ } else {
2964
+ struct object_entry *e = parse_treeish_dataref(&p);
2965
+ root = new_tree_entry();
2966
+ hashcpy(root->versions[1].sha1, e->idx.sha1);
2967
+ load_tree(root);
2968
+ if (*p++ != ' ')
2969
+ die("Missing space after tree-ish: %s", command_buf.buf);
2970
+ }
2971
+ if (*p == '"') {
2972
+ static struct strbuf uq = STRBUF_INIT;
2973
+ const char *endp;
2974
+ strbuf_reset(&uq);
2975
+ if (unquote_c_style(&uq, p, &endp))
2976
+ die("Invalid path: %s", command_buf.buf);
2977
+ if (*endp)
2978
+ die("Garbage after path in: %s", command_buf.buf);
2979
+ p = uq.buf;
2980
+ }
2981
+ tree_content_get(root, p, &leaf);
2982
+ /*
2983
+ * A directory in preparation would have a sha1 of zero
2984
+ * until it is saved. Save, for simplicity.
2985
+ */
2986
+ if (S_ISDIR(leaf.versions[1].mode))
2987
+ store_tree(&leaf);
2988
+
2989
+ print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
2990
+ if (!b || root != &b->branch_tree)
2991
+ release_tree_entry(root);
2992
+}
2993
+
2994
static void checkpoint(void)
2995
{
2996
checkpoint_requested = 0;
3155
relative_marks_paths = 0;
3156
} else if (!prefixcmp(feature, "force")) {
3157
force_update = 1;
3004
- } else if (!strcmp(feature, "notes")) {
3158
+ } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
3159
; /* do nothing; we have the feature */
3160
} else {
3161
return 0;
3296
while (read_next_command() != EOF) {
3297
if (!strcmp("blob", command_buf.buf))
3298
parse_new_blob();
3299
+ else if (!prefixcmp(command_buf.buf, "ls "))
3300
+ parse_ls(NULL);
3301
else if (!prefixcmp(command_buf.buf, "commit "))
3302
parse_new_commit();
3303
else if (!prefixcmp(command_buf.buf, "tag "))