Skip to content

Commit 7c679c8

Browse files
jltoblergitster
authored andcommitted
rev-list: support delimiting objects with NUL bytes
When walking objects, git-rev-list(1) prints each object entry on a separate line. Some options, such as `--objects`, may print additional information about tree and blob object on the same line in the form: $ git rev-list --objects <rev> <tree/blob oid> SP [<path>] LF Note that in this form the SP is appended regardless of whether the tree or blob object has path information available. Paths containing a newline are also truncated at the newline. Introduce the `-z` option for git-rev-list(1) which reformats the output to use NUL-delimiters between objects and associated info. Each object line uses two NUL bytes to indicate the end of an object entry and a single NUL byte to delimit between object information in the following form: $ git rev-list -z --objects <rev> <oid> [NUL <path>] NUL NUL For now, the `--objects` flag is the only option that can be used in combination with `-z`. In this mode, the object path is not truncated at newlines. In a subsequent commit, NUL-delimiter support for other options is added. Other options that do not make sense with be used in combination with `-z` are rejected. Signed-off-by: Justin Tobler <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0685500 commit 7c679c8

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

Documentation/rev-list-options.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,24 @@ ifdef::git-rev-list[]
361361
--progress=<header>::
362362
Show progress reports on stderr as objects are considered. The
363363
`<header>` text will be printed with each progress update.
364+
365+
-z::
366+
Instead of being newline-delimited, each outputted object is delimited
367+
with two NUL bytes in the following form:
368+
+
369+
-----------------------------------------------------------------------
370+
<OID> NUL NUL
371+
-----------------------------------------------------------------------
372+
+
373+
When the `--objects` option is also present, available object name information
374+
is printed in the following form without any truncation for object names
375+
containing newline characters:
376+
+
377+
-----------------------------------------------------------------------
378+
<OID> [NUL <object-name>] NUL NUL
379+
-----------------------------------------------------------------------
380+
+
381+
This option is only compatible with `--objects`.
364382
endif::git-rev-list[]
365383

366384
History Simplification

builtin/rev-list.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static const char rev_list_usage[] =
6565
" --abbrev-commit\n"
6666
" --left-right\n"
6767
" --count\n"
68+
" -z\n"
6869
" special purpose:\n"
6970
" --bisect\n"
7071
" --bisect-vars\n"
@@ -97,10 +98,23 @@ static int arg_show_object_names = 1;
9798

9899
#define DEFAULT_OIDSET_SIZE (16*1024)
99100

101+
static int nul_delim;
100102
static int show_disk_usage;
101103
static off_t total_disk_usage;
102104
static int human_readable;
103105

106+
static void print_object_term(int nul_delim)
107+
{
108+
char line_sep = '\n';
109+
110+
if (nul_delim)
111+
line_sep = '\0';
112+
113+
putchar(line_sep);
114+
if (nul_delim)
115+
putchar(line_sep);
116+
}
117+
104118
static off_t get_object_disk_usage(struct object *obj)
105119
{
106120
off_t size;
@@ -264,7 +278,7 @@ static void show_commit(struct commit *commit, void *data)
264278
if (revs->commit_format == CMIT_FMT_ONELINE)
265279
putchar(' ');
266280
else if (revs->include_header)
267-
putchar('\n');
281+
print_object_term(nul_delim);
268282

269283
if (revs->verbose_header) {
270284
struct strbuf buf = STRBUF_INIT;
@@ -361,12 +375,17 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
361375
printf("%s", oid_to_hex(&obj->oid));
362376

363377
if (arg_show_object_names) {
364-
putchar(' ');
365-
for (const char *p = name; *p && *p != '\n'; p++)
366-
putchar(*p);
378+
if (nul_delim && *name) {
379+
putchar('\0');
380+
printf("%s", name);
381+
} else if (!nul_delim) {
382+
putchar(' ');
383+
for (const char *p = name; *p && *p != '\n'; p++)
384+
putchar(*p);
385+
}
367386
}
368387

369-
putchar('\n');
388+
print_object_term(nul_delim);
370389
}
371390

372391
static void show_edge(struct commit *commit)
@@ -642,6 +661,8 @@ int cmd_rev_list(int argc,
642661
revs.exclude_promisor_objects = 1;
643662
} else if (skip_prefix(arg, "--missing=", &arg)) {
644663
parse_missing_action_value(arg);
664+
} else if (!strcmp(arg, "-z")) {
665+
nul_delim = 1;
645666
}
646667
}
647668

@@ -757,6 +778,14 @@ int cmd_rev_list(int argc,
757778
usage(rev_list_usage);
758779

759780
}
781+
782+
if (nul_delim) {
783+
if (revs.graph || revs.verbose_header || show_disk_usage ||
784+
info.show_timestamp || info.header_prefix || bisect_list ||
785+
use_bitmap_index || revs.edge_hint || arg_missing_action)
786+
die(_("-z option used with unsupported option"));
787+
}
788+
760789
if (revs.commit_format != CMIT_FMT_USERFORMAT)
761790
revs.include_header = 1;
762791
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {

t/t6000-rev-list-misc.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,38 @@ test_expect_success 'rev-list --unpacked' '
182182
test_cmp expect actual
183183
'
184184

185+
test_expect_success 'rev-list -z' '
186+
test_when_finished rm -rf repo &&
187+
188+
git init repo &&
189+
test_commit -C repo 1 &&
190+
test_commit -C repo 2 &&
191+
192+
oid1=$(git -C repo rev-parse HEAD) &&
193+
oid2=$(git -C repo rev-parse HEAD~) &&
194+
195+
printf "%s\0\0%s\0\0" "$oid1" "$oid2" >expect &&
196+
git -C repo rev-list -z HEAD >actual &&
197+
198+
test_cmp expect actual
199+
'
200+
201+
test_expect_success 'rev-list -z --objects' '
202+
test_when_finished rm -rf repo &&
203+
204+
git init repo &&
205+
test_commit -C repo 1 &&
206+
test_commit -C repo 2 &&
207+
208+
oid1=$(git -C repo rev-parse HEAD:1.t) &&
209+
oid2=$(git -C repo rev-parse HEAD:2.t) &&
210+
path1=1.t &&
211+
path2=2.t &&
212+
213+
printf "%s\0%s\0\0%s\0%s\0\0" "$oid1" "$path1" "$oid2" "$path2" >expect &&
214+
git -C repo rev-list -z --objects HEAD:1.t HEAD:2.t >actual &&
215+
216+
test_cmp expect actual
217+
'
218+
185219
test_done

0 commit comments

Comments
 (0)