From fa2656f1da2c01e139423d98b7cbc52cbeadb52c Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:16 -0700 Subject: write_or_die.c: rename to use dashes in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Signed-off-by: Stefan Beller --- Makefile | 2 +- write-or-die.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ write_or_die.c | 71 ---------------------------------------------------------- 3 files changed, 72 insertions(+), 72 deletions(-) create mode 100644 write-or-die.c delete mode 100644 write_or_die.c diff --git a/Makefile b/Makefile index f181687250..f83b796831 100644 --- a/Makefile +++ b/Makefile @@ -936,7 +936,7 @@ LIB_OBJS += walker.o LIB_OBJS += wildmatch.o LIB_OBJS += worktree.o LIB_OBJS += wrapper.o -LIB_OBJS += write_or_die.o +LIB_OBJS += write-or-die.o LIB_OBJS += ws.o LIB_OBJS += wt-status.o LIB_OBJS += xdiff-interface.o diff --git a/write-or-die.c b/write-or-die.c new file mode 100644 index 0000000000..eab8c8d0b9 --- /dev/null +++ b/write-or-die.c @@ -0,0 +1,71 @@ +#include "cache.h" +#include "run-command.h" + +/* + * Some cases use stdio, but want to flush after the write + * to get error handling (and to get better interactive + * behaviour - not buffering excessively). + * + * Of course, if the flush happened within the write itself, + * we've already lost the error code, and cannot report it any + * more. So we just ignore that case instead (and hope we get + * the right error code on the flush). + * + * If the file handle is stdout, and stdout is a file, then skip the + * flush entirely since it's not needed. + */ +void maybe_flush_or_die(FILE *f, const char *desc) +{ + static int skip_stdout_flush = -1; + struct stat st; + char *cp; + + if (f == stdout) { + if (skip_stdout_flush < 0) { + cp = getenv("GIT_FLUSH"); + if (cp) + skip_stdout_flush = (atoi(cp) == 0); + else if ((fstat(fileno(stdout), &st) == 0) && + S_ISREG(st.st_mode)) + skip_stdout_flush = 1; + else + skip_stdout_flush = 0; + } + if (skip_stdout_flush && !ferror(f)) + return; + } + if (fflush(f)) { + check_pipe(errno); + die_errno("write failure on '%s'", desc); + } +} + +void fprintf_or_die(FILE *f, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfprintf(f, fmt, ap); + va_end(ap); + + if (ret < 0) { + check_pipe(errno); + die_errno("write error"); + } +} + +void fsync_or_die(int fd, const char *msg) +{ + if (fsync(fd) < 0) { + die_errno("fsync error on '%s'", msg); + } +} + +void write_or_die(int fd, const void *buf, size_t count) +{ + if (write_in_full(fd, buf, count) < 0) { + check_pipe(errno); + die_errno("write error"); + } +} diff --git a/write_or_die.c b/write_or_die.c deleted file mode 100644 index eab8c8d0b9..0000000000 --- a/write_or_die.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "cache.h" -#include "run-command.h" - -/* - * Some cases use stdio, but want to flush after the write - * to get error handling (and to get better interactive - * behaviour - not buffering excessively). - * - * Of course, if the flush happened within the write itself, - * we've already lost the error code, and cannot report it any - * more. So we just ignore that case instead (and hope we get - * the right error code on the flush). - * - * If the file handle is stdout, and stdout is a file, then skip the - * flush entirely since it's not needed. - */ -void maybe_flush_or_die(FILE *f, const char *desc) -{ - static int skip_stdout_flush = -1; - struct stat st; - char *cp; - - if (f == stdout) { - if (skip_stdout_flush < 0) { - cp = getenv("GIT_FLUSH"); - if (cp) - skip_stdout_flush = (atoi(cp) == 0); - else if ((fstat(fileno(stdout), &st) == 0) && - S_ISREG(st.st_mode)) - skip_stdout_flush = 1; - else - skip_stdout_flush = 0; - } - if (skip_stdout_flush && !ferror(f)) - return; - } - if (fflush(f)) { - check_pipe(errno); - die_errno("write failure on '%s'", desc); - } -} - -void fprintf_or_die(FILE *f, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vfprintf(f, fmt, ap); - va_end(ap); - - if (ret < 0) { - check_pipe(errno); - die_errno("write error"); - } -} - -void fsync_or_die(int fd, const char *msg) -{ - if (fsync(fd) < 0) { - die_errno("fsync error on '%s'", msg); - } -} - -void write_or_die(int fd, const void *buf, size_t count) -{ - if (write_in_full(fd, buf, count) < 0) { - check_pipe(errno); - die_errno("write error"); - } -} -- cgit v1.2.1 From e233bef43e4a73ec183eb927ab4803aefc77bab9 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:17 -0700 Subject: unicode_width.h: rename to use dash in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Also adjust contrib/update-unicode as well. Signed-off-by: Stefan Beller --- contrib/update-unicode/README | 6 +- contrib/update-unicode/update_unicode.sh | 2 +- unicode-width.h | 422 +++++++++++++++++++++++++++++++ unicode_width.h | 422 ------------------------------- utf8.c | 2 +- 5 files changed, 427 insertions(+), 427 deletions(-) create mode 100644 unicode-width.h delete mode 100644 unicode_width.h diff --git a/contrib/update-unicode/README b/contrib/update-unicode/README index b9e2fc8540..151a197041 100644 --- a/contrib/update-unicode/README +++ b/contrib/update-unicode/README @@ -1,10 +1,10 @@ TL;DR: Run update_unicode.sh after the publication of a new Unicode -standard and commit the resulting unicode_widths.h file. +standard and commit the resulting unicode-widths.h file. The long version ================ -The Git source code ships the file unicode_widths.h which contains +The Git source code ships the file unicode-widths.h which contains tables of zero and double width Unicode code points, respectively. These tables are generated using update_unicode.sh in this directory. update_unicode.sh itself uses a third-party tool, uniset, to query two @@ -16,5 +16,5 @@ This requires a current-ish version of autoconf (2.69 works per December On each run, update_unicode.sh checks whether more recent Unicode data files are available from the Unicode consortium, and rebuilds the header -unicode_widths.h with the new data. The new header can then be +unicode-widths.h with the new data. The new header can then be committed. diff --git a/contrib/update-unicode/update_unicode.sh b/contrib/update-unicode/update_unicode.sh index e05db92d3f..aa90865bef 100755 --- a/contrib/update-unicode/update_unicode.sh +++ b/contrib/update-unicode/update_unicode.sh @@ -6,7 +6,7 @@ #Cf Format a format control character # cd "$(dirname "$0")" -UNICODEWIDTH_H=$(git rev-parse --show-toplevel)/unicode_width.h +UNICODEWIDTH_H=$(git rev-parse --show-toplevel)/unicode-width.h wget -N http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt \ http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt && diff --git a/unicode-width.h b/unicode-width.h new file mode 100644 index 0000000000..6dee2c77ce --- /dev/null +++ b/unicode-width.h @@ -0,0 +1,422 @@ +static const struct interval zero_width[] = { +{ 0x0300, 0x036F }, +{ 0x0483, 0x0489 }, +{ 0x0591, 0x05BD }, +{ 0x05BF, 0x05BF }, +{ 0x05C1, 0x05C2 }, +{ 0x05C4, 0x05C5 }, +{ 0x05C7, 0x05C7 }, +{ 0x0600, 0x0605 }, +{ 0x0610, 0x061A }, +{ 0x061C, 0x061C }, +{ 0x064B, 0x065F }, +{ 0x0670, 0x0670 }, +{ 0x06D6, 0x06DD }, +{ 0x06DF, 0x06E4 }, +{ 0x06E7, 0x06E8 }, +{ 0x06EA, 0x06ED }, +{ 0x070F, 0x070F }, +{ 0x0711, 0x0711 }, +{ 0x0730, 0x074A }, +{ 0x07A6, 0x07B0 }, +{ 0x07EB, 0x07F3 }, +{ 0x0816, 0x0819 }, +{ 0x081B, 0x0823 }, +{ 0x0825, 0x0827 }, +{ 0x0829, 0x082D }, +{ 0x0859, 0x085B }, +{ 0x08D4, 0x0902 }, +{ 0x093A, 0x093A }, +{ 0x093C, 0x093C }, +{ 0x0941, 0x0948 }, +{ 0x094D, 0x094D }, +{ 0x0951, 0x0957 }, +{ 0x0962, 0x0963 }, +{ 0x0981, 0x0981 }, +{ 0x09BC, 0x09BC }, +{ 0x09C1, 0x09C4 }, +{ 0x09CD, 0x09CD }, +{ 0x09E2, 0x09E3 }, +{ 0x0A01, 0x0A02 }, +{ 0x0A3C, 0x0A3C }, +{ 0x0A41, 0x0A42 }, +{ 0x0A47, 0x0A48 }, +{ 0x0A4B, 0x0A4D }, +{ 0x0A51, 0x0A51 }, +{ 0x0A70, 0x0A71 }, +{ 0x0A75, 0x0A75 }, +{ 0x0A81, 0x0A82 }, +{ 0x0ABC, 0x0ABC }, +{ 0x0AC1, 0x0AC5 }, +{ 0x0AC7, 0x0AC8 }, +{ 0x0ACD, 0x0ACD }, +{ 0x0AE2, 0x0AE3 }, +{ 0x0AFA, 0x0AFF }, +{ 0x0B01, 0x0B01 }, +{ 0x0B3C, 0x0B3C }, +{ 0x0B3F, 0x0B3F }, +{ 0x0B41, 0x0B44 }, +{ 0x0B4D, 0x0B4D }, +{ 0x0B56, 0x0B56 }, +{ 0x0B62, 0x0B63 }, +{ 0x0B82, 0x0B82 }, +{ 0x0BC0, 0x0BC0 }, +{ 0x0BCD, 0x0BCD }, +{ 0x0C00, 0x0C00 }, +{ 0x0C3E, 0x0C40 }, +{ 0x0C46, 0x0C48 }, +{ 0x0C4A, 0x0C4D }, +{ 0x0C55, 0x0C56 }, +{ 0x0C62, 0x0C63 }, +{ 0x0C81, 0x0C81 }, +{ 0x0CBC, 0x0CBC }, +{ 0x0CBF, 0x0CBF }, +{ 0x0CC6, 0x0CC6 }, +{ 0x0CCC, 0x0CCD }, +{ 0x0CE2, 0x0CE3 }, +{ 0x0D00, 0x0D01 }, +{ 0x0D3B, 0x0D3C }, +{ 0x0D41, 0x0D44 }, +{ 0x0D4D, 0x0D4D }, +{ 0x0D62, 0x0D63 }, +{ 0x0DCA, 0x0DCA }, +{ 0x0DD2, 0x0DD4 }, +{ 0x0DD6, 0x0DD6 }, +{ 0x0E31, 0x0E31 }, +{ 0x0E34, 0x0E3A }, +{ 0x0E47, 0x0E4E }, +{ 0x0EB1, 0x0EB1 }, +{ 0x0EB4, 0x0EB9 }, +{ 0x0EBB, 0x0EBC }, +{ 0x0EC8, 0x0ECD }, +{ 0x0F18, 0x0F19 }, +{ 0x0F35, 0x0F35 }, +{ 0x0F37, 0x0F37 }, +{ 0x0F39, 0x0F39 }, +{ 0x0F71, 0x0F7E }, +{ 0x0F80, 0x0F84 }, +{ 0x0F86, 0x0F87 }, +{ 0x0F8D, 0x0F97 }, +{ 0x0F99, 0x0FBC }, +{ 0x0FC6, 0x0FC6 }, +{ 0x102D, 0x1030 }, +{ 0x1032, 0x1037 }, +{ 0x1039, 0x103A }, +{ 0x103D, 0x103E }, +{ 0x1058, 0x1059 }, +{ 0x105E, 0x1060 }, +{ 0x1071, 0x1074 }, +{ 0x1082, 0x1082 }, +{ 0x1085, 0x1086 }, +{ 0x108D, 0x108D }, +{ 0x109D, 0x109D }, +{ 0x1160, 0x11FF }, +{ 0x135D, 0x135F }, +{ 0x1712, 0x1714 }, +{ 0x1732, 0x1734 }, +{ 0x1752, 0x1753 }, +{ 0x1772, 0x1773 }, +{ 0x17B4, 0x17B5 }, +{ 0x17B7, 0x17BD }, +{ 0x17C6, 0x17C6 }, +{ 0x17C9, 0x17D3 }, +{ 0x17DD, 0x17DD }, +{ 0x180B, 0x180E }, +{ 0x1885, 0x1886 }, +{ 0x18A9, 0x18A9 }, +{ 0x1920, 0x1922 }, +{ 0x1927, 0x1928 }, +{ 0x1932, 0x1932 }, +{ 0x1939, 0x193B }, +{ 0x1A17, 0x1A18 }, +{ 0x1A1B, 0x1A1B }, +{ 0x1A56, 0x1A56 }, +{ 0x1A58, 0x1A5E }, +{ 0x1A60, 0x1A60 }, +{ 0x1A62, 0x1A62 }, +{ 0x1A65, 0x1A6C }, +{ 0x1A73, 0x1A7C }, +{ 0x1A7F, 0x1A7F }, +{ 0x1AB0, 0x1ABE }, +{ 0x1B00, 0x1B03 }, +{ 0x1B34, 0x1B34 }, +{ 0x1B36, 0x1B3A }, +{ 0x1B3C, 0x1B3C }, +{ 0x1B42, 0x1B42 }, +{ 0x1B6B, 0x1B73 }, +{ 0x1B80, 0x1B81 }, +{ 0x1BA2, 0x1BA5 }, +{ 0x1BA8, 0x1BA9 }, +{ 0x1BAB, 0x1BAD }, +{ 0x1BE6, 0x1BE6 }, +{ 0x1BE8, 0x1BE9 }, +{ 0x1BED, 0x1BED }, +{ 0x1BEF, 0x1BF1 }, +{ 0x1C2C, 0x1C33 }, +{ 0x1C36, 0x1C37 }, +{ 0x1CD0, 0x1CD2 }, +{ 0x1CD4, 0x1CE0 }, +{ 0x1CE2, 0x1CE8 }, +{ 0x1CED, 0x1CED }, +{ 0x1CF4, 0x1CF4 }, +{ 0x1CF8, 0x1CF9 }, +{ 0x1DC0, 0x1DF9 }, +{ 0x1DFB, 0x1DFF }, +{ 0x200B, 0x200F }, +{ 0x202A, 0x202E }, +{ 0x2060, 0x2064 }, +{ 0x2066, 0x206F }, +{ 0x20D0, 0x20F0 }, +{ 0x2CEF, 0x2CF1 }, +{ 0x2D7F, 0x2D7F }, +{ 0x2DE0, 0x2DFF }, +{ 0x302A, 0x302D }, +{ 0x3099, 0x309A }, +{ 0xA66F, 0xA672 }, +{ 0xA674, 0xA67D }, +{ 0xA69E, 0xA69F }, +{ 0xA6F0, 0xA6F1 }, +{ 0xA802, 0xA802 }, +{ 0xA806, 0xA806 }, +{ 0xA80B, 0xA80B }, +{ 0xA825, 0xA826 }, +{ 0xA8C4, 0xA8C5 }, +{ 0xA8E0, 0xA8F1 }, +{ 0xA926, 0xA92D }, +{ 0xA947, 0xA951 }, +{ 0xA980, 0xA982 }, +{ 0xA9B3, 0xA9B3 }, +{ 0xA9B6, 0xA9B9 }, +{ 0xA9BC, 0xA9BC }, +{ 0xA9E5, 0xA9E5 }, +{ 0xAA29, 0xAA2E }, +{ 0xAA31, 0xAA32 }, +{ 0xAA35, 0xAA36 }, +{ 0xAA43, 0xAA43 }, +{ 0xAA4C, 0xAA4C }, +{ 0xAA7C, 0xAA7C }, +{ 0xAAB0, 0xAAB0 }, +{ 0xAAB2, 0xAAB4 }, +{ 0xAAB7, 0xAAB8 }, +{ 0xAABE, 0xAABF }, +{ 0xAAC1, 0xAAC1 }, +{ 0xAAEC, 0xAAED }, +{ 0xAAF6, 0xAAF6 }, +{ 0xABE5, 0xABE5 }, +{ 0xABE8, 0xABE8 }, +{ 0xABED, 0xABED }, +{ 0xFB1E, 0xFB1E }, +{ 0xFE00, 0xFE0F }, +{ 0xFE20, 0xFE2F }, +{ 0xFEFF, 0xFEFF }, +{ 0xFFF9, 0xFFFB }, +{ 0x101FD, 0x101FD }, +{ 0x102E0, 0x102E0 }, +{ 0x10376, 0x1037A }, +{ 0x10A01, 0x10A03 }, +{ 0x10A05, 0x10A06 }, +{ 0x10A0C, 0x10A0F }, +{ 0x10A38, 0x10A3A }, +{ 0x10A3F, 0x10A3F }, +{ 0x10AE5, 0x10AE6 }, +{ 0x11001, 0x11001 }, +{ 0x11038, 0x11046 }, +{ 0x1107F, 0x11081 }, +{ 0x110B3, 0x110B6 }, +{ 0x110B9, 0x110BA }, +{ 0x110BD, 0x110BD }, +{ 0x11100, 0x11102 }, +{ 0x11127, 0x1112B }, +{ 0x1112D, 0x11134 }, +{ 0x11173, 0x11173 }, +{ 0x11180, 0x11181 }, +{ 0x111B6, 0x111BE }, +{ 0x111CA, 0x111CC }, +{ 0x1122F, 0x11231 }, +{ 0x11234, 0x11234 }, +{ 0x11236, 0x11237 }, +{ 0x1123E, 0x1123E }, +{ 0x112DF, 0x112DF }, +{ 0x112E3, 0x112EA }, +{ 0x11300, 0x11301 }, +{ 0x1133C, 0x1133C }, +{ 0x11340, 0x11340 }, +{ 0x11366, 0x1136C }, +{ 0x11370, 0x11374 }, +{ 0x11438, 0x1143F }, +{ 0x11442, 0x11444 }, +{ 0x11446, 0x11446 }, +{ 0x114B3, 0x114B8 }, +{ 0x114BA, 0x114BA }, +{ 0x114BF, 0x114C0 }, +{ 0x114C2, 0x114C3 }, +{ 0x115B2, 0x115B5 }, +{ 0x115BC, 0x115BD }, +{ 0x115BF, 0x115C0 }, +{ 0x115DC, 0x115DD }, +{ 0x11633, 0x1163A }, +{ 0x1163D, 0x1163D }, +{ 0x1163F, 0x11640 }, +{ 0x116AB, 0x116AB }, +{ 0x116AD, 0x116AD }, +{ 0x116B0, 0x116B5 }, +{ 0x116B7, 0x116B7 }, +{ 0x1171D, 0x1171F }, +{ 0x11722, 0x11725 }, +{ 0x11727, 0x1172B }, +{ 0x11A01, 0x11A06 }, +{ 0x11A09, 0x11A0A }, +{ 0x11A33, 0x11A38 }, +{ 0x11A3B, 0x11A3E }, +{ 0x11A47, 0x11A47 }, +{ 0x11A51, 0x11A56 }, +{ 0x11A59, 0x11A5B }, +{ 0x11A8A, 0x11A96 }, +{ 0x11A98, 0x11A99 }, +{ 0x11C30, 0x11C36 }, +{ 0x11C38, 0x11C3D }, +{ 0x11C3F, 0x11C3F }, +{ 0x11C92, 0x11CA7 }, +{ 0x11CAA, 0x11CB0 }, +{ 0x11CB2, 0x11CB3 }, +{ 0x11CB5, 0x11CB6 }, +{ 0x11D31, 0x11D36 }, +{ 0x11D3A, 0x11D3A }, +{ 0x11D3C, 0x11D3D }, +{ 0x11D3F, 0x11D45 }, +{ 0x11D47, 0x11D47 }, +{ 0x16AF0, 0x16AF4 }, +{ 0x16B30, 0x16B36 }, +{ 0x16F8F, 0x16F92 }, +{ 0x1BC9D, 0x1BC9E }, +{ 0x1BCA0, 0x1BCA3 }, +{ 0x1D167, 0x1D169 }, +{ 0x1D173, 0x1D182 }, +{ 0x1D185, 0x1D18B }, +{ 0x1D1AA, 0x1D1AD }, +{ 0x1D242, 0x1D244 }, +{ 0x1DA00, 0x1DA36 }, +{ 0x1DA3B, 0x1DA6C }, +{ 0x1DA75, 0x1DA75 }, +{ 0x1DA84, 0x1DA84 }, +{ 0x1DA9B, 0x1DA9F }, +{ 0x1DAA1, 0x1DAAF }, +{ 0x1E000, 0x1E006 }, +{ 0x1E008, 0x1E018 }, +{ 0x1E01B, 0x1E021 }, +{ 0x1E023, 0x1E024 }, +{ 0x1E026, 0x1E02A }, +{ 0x1E8D0, 0x1E8D6 }, +{ 0x1E944, 0x1E94A }, +{ 0xE0001, 0xE0001 }, +{ 0xE0020, 0xE007F }, +{ 0xE0100, 0xE01EF } +}; +static const struct interval double_width[] = { +{ 0x1100, 0x115F }, +{ 0x231A, 0x231B }, +{ 0x2329, 0x232A }, +{ 0x23E9, 0x23EC }, +{ 0x23F0, 0x23F0 }, +{ 0x23F3, 0x23F3 }, +{ 0x25FD, 0x25FE }, +{ 0x2614, 0x2615 }, +{ 0x2648, 0x2653 }, +{ 0x267F, 0x267F }, +{ 0x2693, 0x2693 }, +{ 0x26A1, 0x26A1 }, +{ 0x26AA, 0x26AB }, +{ 0x26BD, 0x26BE }, +{ 0x26C4, 0x26C5 }, +{ 0x26CE, 0x26CE }, +{ 0x26D4, 0x26D4 }, +{ 0x26EA, 0x26EA }, +{ 0x26F2, 0x26F3 }, +{ 0x26F5, 0x26F5 }, +{ 0x26FA, 0x26FA }, +{ 0x26FD, 0x26FD }, +{ 0x2705, 0x2705 }, +{ 0x270A, 0x270B }, +{ 0x2728, 0x2728 }, +{ 0x274C, 0x274C }, +{ 0x274E, 0x274E }, +{ 0x2753, 0x2755 }, +{ 0x2757, 0x2757 }, +{ 0x2795, 0x2797 }, +{ 0x27B0, 0x27B0 }, +{ 0x27BF, 0x27BF }, +{ 0x2B1B, 0x2B1C }, +{ 0x2B50, 0x2B50 }, +{ 0x2B55, 0x2B55 }, +{ 0x2E80, 0x2E99 }, +{ 0x2E9B, 0x2EF3 }, +{ 0x2F00, 0x2FD5 }, +{ 0x2FF0, 0x2FFB }, +{ 0x3000, 0x303E }, +{ 0x3041, 0x3096 }, +{ 0x3099, 0x30FF }, +{ 0x3105, 0x312E }, +{ 0x3131, 0x318E }, +{ 0x3190, 0x31BA }, +{ 0x31C0, 0x31E3 }, +{ 0x31F0, 0x321E }, +{ 0x3220, 0x3247 }, +{ 0x3250, 0x32FE }, +{ 0x3300, 0x4DBF }, +{ 0x4E00, 0xA48C }, +{ 0xA490, 0xA4C6 }, +{ 0xA960, 0xA97C }, +{ 0xAC00, 0xD7A3 }, +{ 0xF900, 0xFAFF }, +{ 0xFE10, 0xFE19 }, +{ 0xFE30, 0xFE52 }, +{ 0xFE54, 0xFE66 }, +{ 0xFE68, 0xFE6B }, +{ 0xFF01, 0xFF60 }, +{ 0xFFE0, 0xFFE6 }, +{ 0x16FE0, 0x16FE1 }, +{ 0x17000, 0x187EC }, +{ 0x18800, 0x18AF2 }, +{ 0x1B000, 0x1B11E }, +{ 0x1B170, 0x1B2FB }, +{ 0x1F004, 0x1F004 }, +{ 0x1F0CF, 0x1F0CF }, +{ 0x1F18E, 0x1F18E }, +{ 0x1F191, 0x1F19A }, +{ 0x1F200, 0x1F202 }, +{ 0x1F210, 0x1F23B }, +{ 0x1F240, 0x1F248 }, +{ 0x1F250, 0x1F251 }, +{ 0x1F260, 0x1F265 }, +{ 0x1F300, 0x1F320 }, +{ 0x1F32D, 0x1F335 }, +{ 0x1F337, 0x1F37C }, +{ 0x1F37E, 0x1F393 }, +{ 0x1F3A0, 0x1F3CA }, +{ 0x1F3CF, 0x1F3D3 }, +{ 0x1F3E0, 0x1F3F0 }, +{ 0x1F3F4, 0x1F3F4 }, +{ 0x1F3F8, 0x1F43E }, +{ 0x1F440, 0x1F440 }, +{ 0x1F442, 0x1F4FC }, +{ 0x1F4FF, 0x1F53D }, +{ 0x1F54B, 0x1F54E }, +{ 0x1F550, 0x1F567 }, +{ 0x1F57A, 0x1F57A }, +{ 0x1F595, 0x1F596 }, +{ 0x1F5A4, 0x1F5A4 }, +{ 0x1F5FB, 0x1F64F }, +{ 0x1F680, 0x1F6C5 }, +{ 0x1F6CC, 0x1F6CC }, +{ 0x1F6D0, 0x1F6D2 }, +{ 0x1F6EB, 0x1F6EC }, +{ 0x1F6F4, 0x1F6F8 }, +{ 0x1F910, 0x1F93E }, +{ 0x1F940, 0x1F94C }, +{ 0x1F950, 0x1F96B }, +{ 0x1F980, 0x1F997 }, +{ 0x1F9C0, 0x1F9C0 }, +{ 0x1F9D0, 0x1F9E6 }, +{ 0x20000, 0x2FFFD }, +{ 0x30000, 0x3FFFD } +}; diff --git a/unicode_width.h b/unicode_width.h deleted file mode 100644 index 6dee2c77ce..0000000000 --- a/unicode_width.h +++ /dev/null @@ -1,422 +0,0 @@ -static const struct interval zero_width[] = { -{ 0x0300, 0x036F }, -{ 0x0483, 0x0489 }, -{ 0x0591, 0x05BD }, -{ 0x05BF, 0x05BF }, -{ 0x05C1, 0x05C2 }, -{ 0x05C4, 0x05C5 }, -{ 0x05C7, 0x05C7 }, -{ 0x0600, 0x0605 }, -{ 0x0610, 0x061A }, -{ 0x061C, 0x061C }, -{ 0x064B, 0x065F }, -{ 0x0670, 0x0670 }, -{ 0x06D6, 0x06DD }, -{ 0x06DF, 0x06E4 }, -{ 0x06E7, 0x06E8 }, -{ 0x06EA, 0x06ED }, -{ 0x070F, 0x070F }, -{ 0x0711, 0x0711 }, -{ 0x0730, 0x074A }, -{ 0x07A6, 0x07B0 }, -{ 0x07EB, 0x07F3 }, -{ 0x0816, 0x0819 }, -{ 0x081B, 0x0823 }, -{ 0x0825, 0x0827 }, -{ 0x0829, 0x082D }, -{ 0x0859, 0x085B }, -{ 0x08D4, 0x0902 }, -{ 0x093A, 0x093A }, -{ 0x093C, 0x093C }, -{ 0x0941, 0x0948 }, -{ 0x094D, 0x094D }, -{ 0x0951, 0x0957 }, -{ 0x0962, 0x0963 }, -{ 0x0981, 0x0981 }, -{ 0x09BC, 0x09BC }, -{ 0x09C1, 0x09C4 }, -{ 0x09CD, 0x09CD }, -{ 0x09E2, 0x09E3 }, -{ 0x0A01, 0x0A02 }, -{ 0x0A3C, 0x0A3C }, -{ 0x0A41, 0x0A42 }, -{ 0x0A47, 0x0A48 }, -{ 0x0A4B, 0x0A4D }, -{ 0x0A51, 0x0A51 }, -{ 0x0A70, 0x0A71 }, -{ 0x0A75, 0x0A75 }, -{ 0x0A81, 0x0A82 }, -{ 0x0ABC, 0x0ABC }, -{ 0x0AC1, 0x0AC5 }, -{ 0x0AC7, 0x0AC8 }, -{ 0x0ACD, 0x0ACD }, -{ 0x0AE2, 0x0AE3 }, -{ 0x0AFA, 0x0AFF }, -{ 0x0B01, 0x0B01 }, -{ 0x0B3C, 0x0B3C }, -{ 0x0B3F, 0x0B3F }, -{ 0x0B41, 0x0B44 }, -{ 0x0B4D, 0x0B4D }, -{ 0x0B56, 0x0B56 }, -{ 0x0B62, 0x0B63 }, -{ 0x0B82, 0x0B82 }, -{ 0x0BC0, 0x0BC0 }, -{ 0x0BCD, 0x0BCD }, -{ 0x0C00, 0x0C00 }, -{ 0x0C3E, 0x0C40 }, -{ 0x0C46, 0x0C48 }, -{ 0x0C4A, 0x0C4D }, -{ 0x0C55, 0x0C56 }, -{ 0x0C62, 0x0C63 }, -{ 0x0C81, 0x0C81 }, -{ 0x0CBC, 0x0CBC }, -{ 0x0CBF, 0x0CBF }, -{ 0x0CC6, 0x0CC6 }, -{ 0x0CCC, 0x0CCD }, -{ 0x0CE2, 0x0CE3 }, -{ 0x0D00, 0x0D01 }, -{ 0x0D3B, 0x0D3C }, -{ 0x0D41, 0x0D44 }, -{ 0x0D4D, 0x0D4D }, -{ 0x0D62, 0x0D63 }, -{ 0x0DCA, 0x0DCA }, -{ 0x0DD2, 0x0DD4 }, -{ 0x0DD6, 0x0DD6 }, -{ 0x0E31, 0x0E31 }, -{ 0x0E34, 0x0E3A }, -{ 0x0E47, 0x0E4E }, -{ 0x0EB1, 0x0EB1 }, -{ 0x0EB4, 0x0EB9 }, -{ 0x0EBB, 0x0EBC }, -{ 0x0EC8, 0x0ECD }, -{ 0x0F18, 0x0F19 }, -{ 0x0F35, 0x0F35 }, -{ 0x0F37, 0x0F37 }, -{ 0x0F39, 0x0F39 }, -{ 0x0F71, 0x0F7E }, -{ 0x0F80, 0x0F84 }, -{ 0x0F86, 0x0F87 }, -{ 0x0F8D, 0x0F97 }, -{ 0x0F99, 0x0FBC }, -{ 0x0FC6, 0x0FC6 }, -{ 0x102D, 0x1030 }, -{ 0x1032, 0x1037 }, -{ 0x1039, 0x103A }, -{ 0x103D, 0x103E }, -{ 0x1058, 0x1059 }, -{ 0x105E, 0x1060 }, -{ 0x1071, 0x1074 }, -{ 0x1082, 0x1082 }, -{ 0x1085, 0x1086 }, -{ 0x108D, 0x108D }, -{ 0x109D, 0x109D }, -{ 0x1160, 0x11FF }, -{ 0x135D, 0x135F }, -{ 0x1712, 0x1714 }, -{ 0x1732, 0x1734 }, -{ 0x1752, 0x1753 }, -{ 0x1772, 0x1773 }, -{ 0x17B4, 0x17B5 }, -{ 0x17B7, 0x17BD }, -{ 0x17C6, 0x17C6 }, -{ 0x17C9, 0x17D3 }, -{ 0x17DD, 0x17DD }, -{ 0x180B, 0x180E }, -{ 0x1885, 0x1886 }, -{ 0x18A9, 0x18A9 }, -{ 0x1920, 0x1922 }, -{ 0x1927, 0x1928 }, -{ 0x1932, 0x1932 }, -{ 0x1939, 0x193B }, -{ 0x1A17, 0x1A18 }, -{ 0x1A1B, 0x1A1B }, -{ 0x1A56, 0x1A56 }, -{ 0x1A58, 0x1A5E }, -{ 0x1A60, 0x1A60 }, -{ 0x1A62, 0x1A62 }, -{ 0x1A65, 0x1A6C }, -{ 0x1A73, 0x1A7C }, -{ 0x1A7F, 0x1A7F }, -{ 0x1AB0, 0x1ABE }, -{ 0x1B00, 0x1B03 }, -{ 0x1B34, 0x1B34 }, -{ 0x1B36, 0x1B3A }, -{ 0x1B3C, 0x1B3C }, -{ 0x1B42, 0x1B42 }, -{ 0x1B6B, 0x1B73 }, -{ 0x1B80, 0x1B81 }, -{ 0x1BA2, 0x1BA5 }, -{ 0x1BA8, 0x1BA9 }, -{ 0x1BAB, 0x1BAD }, -{ 0x1BE6, 0x1BE6 }, -{ 0x1BE8, 0x1BE9 }, -{ 0x1BED, 0x1BED }, -{ 0x1BEF, 0x1BF1 }, -{ 0x1C2C, 0x1C33 }, -{ 0x1C36, 0x1C37 }, -{ 0x1CD0, 0x1CD2 }, -{ 0x1CD4, 0x1CE0 }, -{ 0x1CE2, 0x1CE8 }, -{ 0x1CED, 0x1CED }, -{ 0x1CF4, 0x1CF4 }, -{ 0x1CF8, 0x1CF9 }, -{ 0x1DC0, 0x1DF9 }, -{ 0x1DFB, 0x1DFF }, -{ 0x200B, 0x200F }, -{ 0x202A, 0x202E }, -{ 0x2060, 0x2064 }, -{ 0x2066, 0x206F }, -{ 0x20D0, 0x20F0 }, -{ 0x2CEF, 0x2CF1 }, -{ 0x2D7F, 0x2D7F }, -{ 0x2DE0, 0x2DFF }, -{ 0x302A, 0x302D }, -{ 0x3099, 0x309A }, -{ 0xA66F, 0xA672 }, -{ 0xA674, 0xA67D }, -{ 0xA69E, 0xA69F }, -{ 0xA6F0, 0xA6F1 }, -{ 0xA802, 0xA802 }, -{ 0xA806, 0xA806 }, -{ 0xA80B, 0xA80B }, -{ 0xA825, 0xA826 }, -{ 0xA8C4, 0xA8C5 }, -{ 0xA8E0, 0xA8F1 }, -{ 0xA926, 0xA92D }, -{ 0xA947, 0xA951 }, -{ 0xA980, 0xA982 }, -{ 0xA9B3, 0xA9B3 }, -{ 0xA9B6, 0xA9B9 }, -{ 0xA9BC, 0xA9BC }, -{ 0xA9E5, 0xA9E5 }, -{ 0xAA29, 0xAA2E }, -{ 0xAA31, 0xAA32 }, -{ 0xAA35, 0xAA36 }, -{ 0xAA43, 0xAA43 }, -{ 0xAA4C, 0xAA4C }, -{ 0xAA7C, 0xAA7C }, -{ 0xAAB0, 0xAAB0 }, -{ 0xAAB2, 0xAAB4 }, -{ 0xAAB7, 0xAAB8 }, -{ 0xAABE, 0xAABF }, -{ 0xAAC1, 0xAAC1 }, -{ 0xAAEC, 0xAAED }, -{ 0xAAF6, 0xAAF6 }, -{ 0xABE5, 0xABE5 }, -{ 0xABE8, 0xABE8 }, -{ 0xABED, 0xABED }, -{ 0xFB1E, 0xFB1E }, -{ 0xFE00, 0xFE0F }, -{ 0xFE20, 0xFE2F }, -{ 0xFEFF, 0xFEFF }, -{ 0xFFF9, 0xFFFB }, -{ 0x101FD, 0x101FD }, -{ 0x102E0, 0x102E0 }, -{ 0x10376, 0x1037A }, -{ 0x10A01, 0x10A03 }, -{ 0x10A05, 0x10A06 }, -{ 0x10A0C, 0x10A0F }, -{ 0x10A38, 0x10A3A }, -{ 0x10A3F, 0x10A3F }, -{ 0x10AE5, 0x10AE6 }, -{ 0x11001, 0x11001 }, -{ 0x11038, 0x11046 }, -{ 0x1107F, 0x11081 }, -{ 0x110B3, 0x110B6 }, -{ 0x110B9, 0x110BA }, -{ 0x110BD, 0x110BD }, -{ 0x11100, 0x11102 }, -{ 0x11127, 0x1112B }, -{ 0x1112D, 0x11134 }, -{ 0x11173, 0x11173 }, -{ 0x11180, 0x11181 }, -{ 0x111B6, 0x111BE }, -{ 0x111CA, 0x111CC }, -{ 0x1122F, 0x11231 }, -{ 0x11234, 0x11234 }, -{ 0x11236, 0x11237 }, -{ 0x1123E, 0x1123E }, -{ 0x112DF, 0x112DF }, -{ 0x112E3, 0x112EA }, -{ 0x11300, 0x11301 }, -{ 0x1133C, 0x1133C }, -{ 0x11340, 0x11340 }, -{ 0x11366, 0x1136C }, -{ 0x11370, 0x11374 }, -{ 0x11438, 0x1143F }, -{ 0x11442, 0x11444 }, -{ 0x11446, 0x11446 }, -{ 0x114B3, 0x114B8 }, -{ 0x114BA, 0x114BA }, -{ 0x114BF, 0x114C0 }, -{ 0x114C2, 0x114C3 }, -{ 0x115B2, 0x115B5 }, -{ 0x115BC, 0x115BD }, -{ 0x115BF, 0x115C0 }, -{ 0x115DC, 0x115DD }, -{ 0x11633, 0x1163A }, -{ 0x1163D, 0x1163D }, -{ 0x1163F, 0x11640 }, -{ 0x116AB, 0x116AB }, -{ 0x116AD, 0x116AD }, -{ 0x116B0, 0x116B5 }, -{ 0x116B7, 0x116B7 }, -{ 0x1171D, 0x1171F }, -{ 0x11722, 0x11725 }, -{ 0x11727, 0x1172B }, -{ 0x11A01, 0x11A06 }, -{ 0x11A09, 0x11A0A }, -{ 0x11A33, 0x11A38 }, -{ 0x11A3B, 0x11A3E }, -{ 0x11A47, 0x11A47 }, -{ 0x11A51, 0x11A56 }, -{ 0x11A59, 0x11A5B }, -{ 0x11A8A, 0x11A96 }, -{ 0x11A98, 0x11A99 }, -{ 0x11C30, 0x11C36 }, -{ 0x11C38, 0x11C3D }, -{ 0x11C3F, 0x11C3F }, -{ 0x11C92, 0x11CA7 }, -{ 0x11CAA, 0x11CB0 }, -{ 0x11CB2, 0x11CB3 }, -{ 0x11CB5, 0x11CB6 }, -{ 0x11D31, 0x11D36 }, -{ 0x11D3A, 0x11D3A }, -{ 0x11D3C, 0x11D3D }, -{ 0x11D3F, 0x11D45 }, -{ 0x11D47, 0x11D47 }, -{ 0x16AF0, 0x16AF4 }, -{ 0x16B30, 0x16B36 }, -{ 0x16F8F, 0x16F92 }, -{ 0x1BC9D, 0x1BC9E }, -{ 0x1BCA0, 0x1BCA3 }, -{ 0x1D167, 0x1D169 }, -{ 0x1D173, 0x1D182 }, -{ 0x1D185, 0x1D18B }, -{ 0x1D1AA, 0x1D1AD }, -{ 0x1D242, 0x1D244 }, -{ 0x1DA00, 0x1DA36 }, -{ 0x1DA3B, 0x1DA6C }, -{ 0x1DA75, 0x1DA75 }, -{ 0x1DA84, 0x1DA84 }, -{ 0x1DA9B, 0x1DA9F }, -{ 0x1DAA1, 0x1DAAF }, -{ 0x1E000, 0x1E006 }, -{ 0x1E008, 0x1E018 }, -{ 0x1E01B, 0x1E021 }, -{ 0x1E023, 0x1E024 }, -{ 0x1E026, 0x1E02A }, -{ 0x1E8D0, 0x1E8D6 }, -{ 0x1E944, 0x1E94A }, -{ 0xE0001, 0xE0001 }, -{ 0xE0020, 0xE007F }, -{ 0xE0100, 0xE01EF } -}; -static const struct interval double_width[] = { -{ 0x1100, 0x115F }, -{ 0x231A, 0x231B }, -{ 0x2329, 0x232A }, -{ 0x23E9, 0x23EC }, -{ 0x23F0, 0x23F0 }, -{ 0x23F3, 0x23F3 }, -{ 0x25FD, 0x25FE }, -{ 0x2614, 0x2615 }, -{ 0x2648, 0x2653 }, -{ 0x267F, 0x267F }, -{ 0x2693, 0x2693 }, -{ 0x26A1, 0x26A1 }, -{ 0x26AA, 0x26AB }, -{ 0x26BD, 0x26BE }, -{ 0x26C4, 0x26C5 }, -{ 0x26CE, 0x26CE }, -{ 0x26D4, 0x26D4 }, -{ 0x26EA, 0x26EA }, -{ 0x26F2, 0x26F3 }, -{ 0x26F5, 0x26F5 }, -{ 0x26FA, 0x26FA }, -{ 0x26FD, 0x26FD }, -{ 0x2705, 0x2705 }, -{ 0x270A, 0x270B }, -{ 0x2728, 0x2728 }, -{ 0x274C, 0x274C }, -{ 0x274E, 0x274E }, -{ 0x2753, 0x2755 }, -{ 0x2757, 0x2757 }, -{ 0x2795, 0x2797 }, -{ 0x27B0, 0x27B0 }, -{ 0x27BF, 0x27BF }, -{ 0x2B1B, 0x2B1C }, -{ 0x2B50, 0x2B50 }, -{ 0x2B55, 0x2B55 }, -{ 0x2E80, 0x2E99 }, -{ 0x2E9B, 0x2EF3 }, -{ 0x2F00, 0x2FD5 }, -{ 0x2FF0, 0x2FFB }, -{ 0x3000, 0x303E }, -{ 0x3041, 0x3096 }, -{ 0x3099, 0x30FF }, -{ 0x3105, 0x312E }, -{ 0x3131, 0x318E }, -{ 0x3190, 0x31BA }, -{ 0x31C0, 0x31E3 }, -{ 0x31F0, 0x321E }, -{ 0x3220, 0x3247 }, -{ 0x3250, 0x32FE }, -{ 0x3300, 0x4DBF }, -{ 0x4E00, 0xA48C }, -{ 0xA490, 0xA4C6 }, -{ 0xA960, 0xA97C }, -{ 0xAC00, 0xD7A3 }, -{ 0xF900, 0xFAFF }, -{ 0xFE10, 0xFE19 }, -{ 0xFE30, 0xFE52 }, -{ 0xFE54, 0xFE66 }, -{ 0xFE68, 0xFE6B }, -{ 0xFF01, 0xFF60 }, -{ 0xFFE0, 0xFFE6 }, -{ 0x16FE0, 0x16FE1 }, -{ 0x17000, 0x187EC }, -{ 0x18800, 0x18AF2 }, -{ 0x1B000, 0x1B11E }, -{ 0x1B170, 0x1B2FB }, -{ 0x1F004, 0x1F004 }, -{ 0x1F0CF, 0x1F0CF }, -{ 0x1F18E, 0x1F18E }, -{ 0x1F191, 0x1F19A }, -{ 0x1F200, 0x1F202 }, -{ 0x1F210, 0x1F23B }, -{ 0x1F240, 0x1F248 }, -{ 0x1F250, 0x1F251 }, -{ 0x1F260, 0x1F265 }, -{ 0x1F300, 0x1F320 }, -{ 0x1F32D, 0x1F335 }, -{ 0x1F337, 0x1F37C }, -{ 0x1F37E, 0x1F393 }, -{ 0x1F3A0, 0x1F3CA }, -{ 0x1F3CF, 0x1F3D3 }, -{ 0x1F3E0, 0x1F3F0 }, -{ 0x1F3F4, 0x1F3F4 }, -{ 0x1F3F8, 0x1F43E }, -{ 0x1F440, 0x1F440 }, -{ 0x1F442, 0x1F4FC }, -{ 0x1F4FF, 0x1F53D }, -{ 0x1F54B, 0x1F54E }, -{ 0x1F550, 0x1F567 }, -{ 0x1F57A, 0x1F57A }, -{ 0x1F595, 0x1F596 }, -{ 0x1F5A4, 0x1F5A4 }, -{ 0x1F5FB, 0x1F64F }, -{ 0x1F680, 0x1F6C5 }, -{ 0x1F6CC, 0x1F6CC }, -{ 0x1F6D0, 0x1F6D2 }, -{ 0x1F6EB, 0x1F6EC }, -{ 0x1F6F4, 0x1F6F8 }, -{ 0x1F910, 0x1F93E }, -{ 0x1F940, 0x1F94C }, -{ 0x1F950, 0x1F96B }, -{ 0x1F980, 0x1F997 }, -{ 0x1F9C0, 0x1F9C0 }, -{ 0x1F9D0, 0x1F9E6 }, -{ 0x20000, 0x2FFFD }, -{ 0x30000, 0x3FFFD } -}; diff --git a/utf8.c b/utf8.c index 2c27ce0137..4419055b48 100644 --- a/utf8.c +++ b/utf8.c @@ -81,7 +81,7 @@ static int git_wcwidth(ucs_char_t ch) /* * Sorted list of non-overlapping intervals of non-spacing characters, */ -#include "unicode_width.h" +#include "unicode-width.h" /* test for 8-bit control characters */ if (ch == 0) -- cgit v1.2.1 From d807c4a01db2b06db047fc6d5d18ac25c8f05bd7 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:18 -0700 Subject: exec_cmd: rename to use dash in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Signed-off-by: Stefan Beller --- Makefile | 6 +- attr.c | 2 +- builtin/add.c | 2 +- builtin/am.c | 2 +- builtin/describe.c | 2 +- builtin/difftool.c | 2 +- builtin/hash-object.c | 2 +- builtin/help.c | 2 +- builtin/index-pack.c | 2 +- builtin/init-db.c | 2 +- builtin/merge-tree.c | 2 +- builtin/notes.c | 2 +- builtin/pull.c | 2 +- builtin/receive-pack.c | 2 +- common-main.c | 2 +- config.c | 2 +- exec-cmd.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++ exec-cmd.h | 16 +++++ exec_cmd.c | 165 ------------------------------------------------- exec_cmd.h | 16 ----- fetch-pack.c | 2 +- git.c | 2 +- help.c | 2 +- http-backend.c | 2 +- http-fetch.c | 2 +- http-push.c | 2 +- imap-send.c | 2 +- remote-curl.c | 2 +- remote-testsvn.c | 2 +- run-command.c | 2 +- sequencer.c | 2 +- shell.c | 2 +- upload-pack.c | 2 +- 33 files changed, 212 insertions(+), 212 deletions(-) create mode 100644 exec-cmd.c create mode 100644 exec-cmd.h delete mode 100644 exec_cmd.c delete mode 100644 exec_cmd.h diff --git a/Makefile b/Makefile index f83b796831..88730d6fdd 100644 --- a/Makefile +++ b/Makefile @@ -818,7 +818,7 @@ LIB_OBJS += ewah/bitmap.o LIB_OBJS += ewah/ewah_bitmap.o LIB_OBJS += ewah/ewah_io.o LIB_OBJS += ewah/ewah_rlw.o -LIB_OBJS += exec_cmd.o +LIB_OBJS += exec-cmd.o LIB_OBJS += fetch-object.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o @@ -2155,8 +2155,8 @@ else $(OBJECTS): $(LIB_H) endif -exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX -exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \ +exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX +exec-cmd.sp exec-cmd.s exec-cmd.o: EXTRA_CPPFLAGS = \ '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \ '-DBINDIR="$(bindir_relative_SQ)"' \ '-DPREFIX="$(prefix_SQ)"' diff --git a/attr.c b/attr.c index dfc3a558d8..03a678fa9b 100644 --- a/attr.c +++ b/attr.c @@ -10,7 +10,7 @@ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "attr.h" #include "dir.h" #include "utf8.h" diff --git a/builtin/add.c b/builtin/add.c index 9ef7fb02d5..c9e2619a9a 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -9,7 +9,7 @@ #include "lockfile.h" #include "dir.h" #include "pathspec.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "cache-tree.h" #include "run-command.h" #include "parse-options.h" diff --git a/builtin/am.c b/builtin/am.c index 9c82603f70..d834f9e62b 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -6,7 +6,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "dir.h" #include "run-command.h" diff --git a/builtin/describe.c b/builtin/describe.c index de840f96a4..b5afc45846 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -6,7 +6,7 @@ #include "blob.h" #include "refs.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "revision.h" #include "diff.h" diff --git a/builtin/difftool.c b/builtin/difftool.c index ee8dce019e..aad0e073ee 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -15,7 +15,7 @@ #include "config.h" #include "builtin.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "argv-array.h" #include "strbuf.h" diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 526da5c185..a9a3a198c3 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -9,7 +9,7 @@ #include "blob.h" #include "quote.h" #include "parse-options.h" -#include "exec_cmd.h" +#include "exec-cmd.h" /* * This is to create corrupt objects for debugging and as such it diff --git a/builtin/help.c b/builtin/help.c index 598867cfea..2d51071429 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -4,7 +4,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "run-command.h" #include "column.h" diff --git a/builtin/index-pack.c b/builtin/index-pack.c index d81473e722..7700907f1b 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -9,7 +9,7 @@ #include "tree.h" #include "progress.h" #include "fsck.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "streaming.h" #include "thread-utils.h" #include "packfile.h" diff --git a/builtin/init-db.c b/builtin/init-db.c index 68ff4ad75a..2542c5244e 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -7,7 +7,7 @@ #include "config.h" #include "refs.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 32736e0b10..bf01e05808 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -2,7 +2,7 @@ #include "tree-walk.h" #include "xdiff-interface.h" #include "blob.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "merge-blobs.h" static const char merge_tree_usage[] = "git merge-tree "; diff --git a/builtin/notes.c b/builtin/notes.c index 921e08d5bf..e5bf80eef1 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -14,7 +14,7 @@ #include "blob.h" #include "pretty.h" #include "refs.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/pull.c b/builtin/pull.c index e32d6cd5b4..71aac5005e 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -9,7 +9,7 @@ #include "config.h" #include "builtin.h" #include "parse-options.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "sha1-array.h" #include "remote.h" diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c4272fbc96..4b68a28e92 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -7,7 +7,7 @@ #include "pkt-line.h" #include "sideband.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "commit.h" #include "object.h" #include "remote.h" diff --git a/common-main.c b/common-main.c index 7d716d5a54..b989e136b5 100644 --- a/common-main.c +++ b/common-main.c @@ -1,5 +1,5 @@ #include "cache.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "attr.h" /* diff --git a/config.c b/config.c index c698988f5e..e2b87b4764 100644 --- a/config.c +++ b/config.c @@ -9,7 +9,7 @@ #include "config.h" #include "repository.h" #include "lockfile.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "strbuf.h" #include "quote.h" #include "hashmap.h" diff --git a/exec-cmd.c b/exec-cmd.c new file mode 100644 index 0000000000..8a8261746a --- /dev/null +++ b/exec-cmd.c @@ -0,0 +1,165 @@ +#include "cache.h" +#include "exec-cmd.h" +#include "quote.h" +#include "argv-array.h" +#define MAX_ARGS 32 + +static const char *argv_exec_path; + +#ifdef RUNTIME_PREFIX +static const char *argv0_path; + +static const char *system_prefix(void) +{ + static const char *prefix; + + assert(argv0_path); + assert(is_absolute_path(argv0_path)); + + if (!prefix && + !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) && + !(prefix = strip_path_suffix(argv0_path, BINDIR)) && + !(prefix = strip_path_suffix(argv0_path, "git"))) { + prefix = PREFIX; + trace_printf("RUNTIME_PREFIX requested, " + "but prefix computation failed. " + "Using static fallback '%s'.\n", prefix); + } + return prefix; +} + +void git_extract_argv0_path(const char *argv0) +{ + const char *slash; + + if (!argv0 || !*argv0) + return; + + slash = find_last_dir_sep(argv0); + + if (slash) + argv0_path = xstrndup(argv0, slash - argv0); +} + +#else + +static const char *system_prefix(void) +{ + return PREFIX; +} + +void git_extract_argv0_path(const char *argv0) +{ +} + +#endif /* RUNTIME_PREFIX */ + +char *system_path(const char *path) +{ + struct strbuf d = STRBUF_INIT; + + if (is_absolute_path(path)) + return xstrdup(path); + + strbuf_addf(&d, "%s/%s", system_prefix(), path); + return strbuf_detach(&d, NULL); +} + +void git_set_argv_exec_path(const char *exec_path) +{ + argv_exec_path = exec_path; + /* + * Propagate this setting to external programs. + */ + setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); +} + + +/* Returns the highest-priority, location to look for git programs. */ +const char *git_exec_path(void) +{ + static char *cached_exec_path; + + if (argv_exec_path) + return argv_exec_path; + + if (!cached_exec_path) { + const char *env = getenv(EXEC_PATH_ENVIRONMENT); + if (env && *env) + cached_exec_path = xstrdup(env); + else + cached_exec_path = system_path(GIT_EXEC_PATH); + } + return cached_exec_path; +} + +static void add_path(struct strbuf *out, const char *path) +{ + if (path && *path) { + strbuf_add_absolute_path(out, path); + strbuf_addch(out, PATH_SEP); + } +} + +void setup_path(void) +{ + const char *old_path = getenv("PATH"); + struct strbuf new_path = STRBUF_INIT; + + add_path(&new_path, git_exec_path()); + + if (old_path) + strbuf_addstr(&new_path, old_path); + else + strbuf_addstr(&new_path, _PATH_DEFPATH); + + setenv("PATH", new_path.buf, 1); + + strbuf_release(&new_path); +} + +const char **prepare_git_cmd(struct argv_array *out, const char **argv) +{ + argv_array_push(out, "git"); + argv_array_pushv(out, argv); + return out->argv; +} + +int execv_git_cmd(const char **argv) { + struct argv_array nargv = ARGV_ARRAY_INIT; + + prepare_git_cmd(&nargv, argv); + trace_argv_printf(nargv.argv, "trace: exec:"); + + /* execvp() can only ever return if it fails */ + sane_execvp("git", (char **)nargv.argv); + + trace_printf("trace: exec failed: %s\n", strerror(errno)); + + argv_array_clear(&nargv); + return -1; +} + + +int execl_git_cmd(const char *cmd,...) +{ + int argc; + const char *argv[MAX_ARGS + 1]; + const char *arg; + va_list param; + + va_start(param, cmd); + argv[0] = cmd; + argc = 1; + while (argc < MAX_ARGS) { + arg = argv[argc++] = va_arg(param, char *); + if (!arg) + break; + } + va_end(param); + if (MAX_ARGS <= argc) + return error("too many args to run %s", cmd); + + argv[argc] = NULL; + return execv_git_cmd(argv); +} diff --git a/exec-cmd.h b/exec-cmd.h new file mode 100644 index 0000000000..ff0b48048a --- /dev/null +++ b/exec-cmd.h @@ -0,0 +1,16 @@ +#ifndef GIT_EXEC_CMD_H +#define GIT_EXEC_CMD_H + +struct argv_array; + +extern void git_set_argv_exec_path(const char *exec_path); +extern void git_extract_argv0_path(const char *path); +extern const char *git_exec_path(void); +extern void setup_path(void); +extern const char **prepare_git_cmd(struct argv_array *out, const char **argv); +extern int execv_git_cmd(const char **argv); /* NULL terminated */ +LAST_ARG_MUST_BE_NULL +extern int execl_git_cmd(const char *cmd, ...); +extern char *system_path(const char *path); + +#endif /* GIT_EXEC_CMD_H */ diff --git a/exec_cmd.c b/exec_cmd.c deleted file mode 100644 index ce192a2d64..0000000000 --- a/exec_cmd.c +++ /dev/null @@ -1,165 +0,0 @@ -#include "cache.h" -#include "exec_cmd.h" -#include "quote.h" -#include "argv-array.h" -#define MAX_ARGS 32 - -static const char *argv_exec_path; - -#ifdef RUNTIME_PREFIX -static const char *argv0_path; - -static const char *system_prefix(void) -{ - static const char *prefix; - - assert(argv0_path); - assert(is_absolute_path(argv0_path)); - - if (!prefix && - !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) && - !(prefix = strip_path_suffix(argv0_path, BINDIR)) && - !(prefix = strip_path_suffix(argv0_path, "git"))) { - prefix = PREFIX; - trace_printf("RUNTIME_PREFIX requested, " - "but prefix computation failed. " - "Using static fallback '%s'.\n", prefix); - } - return prefix; -} - -void git_extract_argv0_path(const char *argv0) -{ - const char *slash; - - if (!argv0 || !*argv0) - return; - - slash = find_last_dir_sep(argv0); - - if (slash) - argv0_path = xstrndup(argv0, slash - argv0); -} - -#else - -static const char *system_prefix(void) -{ - return PREFIX; -} - -void git_extract_argv0_path(const char *argv0) -{ -} - -#endif /* RUNTIME_PREFIX */ - -char *system_path(const char *path) -{ - struct strbuf d = STRBUF_INIT; - - if (is_absolute_path(path)) - return xstrdup(path); - - strbuf_addf(&d, "%s/%s", system_prefix(), path); - return strbuf_detach(&d, NULL); -} - -void git_set_argv_exec_path(const char *exec_path) -{ - argv_exec_path = exec_path; - /* - * Propagate this setting to external programs. - */ - setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); -} - - -/* Returns the highest-priority, location to look for git programs. */ -const char *git_exec_path(void) -{ - static char *cached_exec_path; - - if (argv_exec_path) - return argv_exec_path; - - if (!cached_exec_path) { - const char *env = getenv(EXEC_PATH_ENVIRONMENT); - if (env && *env) - cached_exec_path = xstrdup(env); - else - cached_exec_path = system_path(GIT_EXEC_PATH); - } - return cached_exec_path; -} - -static void add_path(struct strbuf *out, const char *path) -{ - if (path && *path) { - strbuf_add_absolute_path(out, path); - strbuf_addch(out, PATH_SEP); - } -} - -void setup_path(void) -{ - const char *old_path = getenv("PATH"); - struct strbuf new_path = STRBUF_INIT; - - add_path(&new_path, git_exec_path()); - - if (old_path) - strbuf_addstr(&new_path, old_path); - else - strbuf_addstr(&new_path, _PATH_DEFPATH); - - setenv("PATH", new_path.buf, 1); - - strbuf_release(&new_path); -} - -const char **prepare_git_cmd(struct argv_array *out, const char **argv) -{ - argv_array_push(out, "git"); - argv_array_pushv(out, argv); - return out->argv; -} - -int execv_git_cmd(const char **argv) { - struct argv_array nargv = ARGV_ARRAY_INIT; - - prepare_git_cmd(&nargv, argv); - trace_argv_printf(nargv.argv, "trace: exec:"); - - /* execvp() can only ever return if it fails */ - sane_execvp("git", (char **)nargv.argv); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); - - argv_array_clear(&nargv); - return -1; -} - - -int execl_git_cmd(const char *cmd,...) -{ - int argc; - const char *argv[MAX_ARGS + 1]; - const char *arg; - va_list param; - - va_start(param, cmd); - argv[0] = cmd; - argc = 1; - while (argc < MAX_ARGS) { - arg = argv[argc++] = va_arg(param, char *); - if (!arg) - break; - } - va_end(param); - if (MAX_ARGS <= argc) - return error("too many args to run %s", cmd); - - argv[argc] = NULL; - return execv_git_cmd(argv); -} diff --git a/exec_cmd.h b/exec_cmd.h deleted file mode 100644 index ff0b48048a..0000000000 --- a/exec_cmd.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef GIT_EXEC_CMD_H -#define GIT_EXEC_CMD_H - -struct argv_array; - -extern void git_set_argv_exec_path(const char *exec_path); -extern void git_extract_argv0_path(const char *path); -extern const char *git_exec_path(void); -extern void setup_path(void); -extern const char **prepare_git_cmd(struct argv_array *out, const char **argv); -extern int execv_git_cmd(const char **argv); /* NULL terminated */ -LAST_ARG_MUST_BE_NULL -extern int execl_git_cmd(const char *cmd, ...); -extern char *system_path(const char *path); - -#endif /* GIT_EXEC_CMD_H */ diff --git a/fetch-pack.c b/fetch-pack.c index adc1b68dd3..4a8bad8487 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -6,7 +6,7 @@ #include "pkt-line.h" #include "commit.h" #include "tag.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "pack.h" #include "sideband.h" #include "fetch-pack.h" diff --git a/git.c b/git.c index 3a89893712..f598fae7b7 100644 --- a/git.c +++ b/git.c @@ -1,6 +1,6 @@ #include "builtin.h" #include "config.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "help.h" #include "run-command.h" diff --git a/help.c b/help.c index 60071a9bea..a4feef2ffe 100644 --- a/help.c +++ b/help.c @@ -1,7 +1,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "levenshtein.h" #include "help.h" diff --git a/http-backend.c b/http-backend.c index 88d2a9bc40..cc16cd04ad 100644 --- a/http-backend.c +++ b/http-backend.c @@ -5,7 +5,7 @@ #include "pkt-line.h" #include "object.h" #include "tag.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "string-list.h" #include "url.h" diff --git a/http-fetch.c b/http-fetch.c index 8af380050c..885e471501 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -1,6 +1,6 @@ #include "cache.h" #include "config.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "http.h" #include "walker.h" diff --git a/http-push.c b/http-push.c index c0998fd763..f308ce0195 100644 --- a/http-push.c +++ b/http-push.c @@ -6,7 +6,7 @@ #include "refs.h" #include "diff.h" #include "revision.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "remote.h" #include "list-objects.h" #include "sigchain.h" diff --git a/imap-send.c b/imap-send.c index ffb0a6eca8..3573cbfb0f 100644 --- a/imap-send.c +++ b/imap-send.c @@ -24,7 +24,7 @@ #include "cache.h" #include "config.h" #include "credential.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "parse-options.h" #ifdef NO_OPENSSL diff --git a/remote-curl.c b/remote-curl.c index a7c4c9b5ff..8d2ffaf8de 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -4,7 +4,7 @@ #include "strbuf.h" #include "walker.h" #include "http.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "pkt-line.h" #include "string-list.h" diff --git a/remote-testsvn.c b/remote-testsvn.c index c4bb9a8ba9..444d98059f 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -3,7 +3,7 @@ #include "remote.h" #include "strbuf.h" #include "url.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "vcs-svn/svndump.h" #include "notes.h" diff --git a/run-command.c b/run-command.c index 84899e423f..12c94c1dbe 100644 --- a/run-command.c +++ b/run-command.c @@ -1,6 +1,6 @@ #include "cache.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "sigchain.h" #include "argv-array.h" #include "thread-utils.h" diff --git a/sequencer.c b/sequencer.c index 667f35ebdf..6d631d25c6 100644 --- a/sequencer.c +++ b/sequencer.c @@ -7,7 +7,7 @@ #include "sequencer.h" #include "tag.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "utf8.h" #include "cache-tree.h" #include "diff.h" diff --git a/shell.c b/shell.c index 234b2d4f16..0200d10796 100644 --- a/shell.c +++ b/shell.c @@ -1,6 +1,6 @@ #include "cache.h" #include "quote.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "strbuf.h" #include "run-command.h" diff --git a/upload-pack.c b/upload-pack.c index 4a82602be5..6261d4fab3 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -6,7 +6,7 @@ #include "tag.h" #include "object.h" #include "commit.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "diff.h" #include "revision.h" #include "list-objects.h" -- cgit v1.2.1 From e5e5e08832f7c4d581c0c70542e61235d21a8067 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:19 -0700 Subject: sha1_name.c: rename to use dash in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Signed-off-by: Stefan Beller --- Makefile | 2 +- list-objects-filter.c | 2 +- object.h | 2 +- sha1-name.c | 1734 +++++++++++++++++++++++++++++++++++++++++++++++++ sha1_name.c | 1734 ------------------------------------------------- 5 files changed, 1737 insertions(+), 1737 deletions(-) create mode 100644 sha1-name.c delete mode 100644 sha1_name.c diff --git a/Makefile b/Makefile index 88730d6fdd..ed0ac30a16 100644 --- a/Makefile +++ b/Makefile @@ -901,7 +901,7 @@ LIB_OBJS += setup.o LIB_OBJS += sha1-array.o LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_file.o -LIB_OBJS += sha1_name.o +LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o LIB_OBJS += sigchain.o diff --git a/list-objects-filter.c b/list-objects-filter.c index 0ec83aaf18..247717561f 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -19,7 +19,7 @@ * in the traversal (until we mark it SEEN). This is a way to * let us silently de-dup calls to show() in the caller. This * is subtly different from the "revision.h:SHOWN" and the - * "sha1_name.c:ONELINE_SEEN" bits. And also different from + * "sha1-name.c:ONELINE_SEEN" bits. And also different from * the non-de-dup usage in pack-bitmap.c */ #define FILTER_SHOWN_BUT_REVISIT (1<<21) diff --git a/object.h b/object.h index f13f85b2a9..b8e70e5519 100644 --- a/object.h +++ b/object.h @@ -37,7 +37,7 @@ struct object_array { * bundle.c: 16 * http-push.c: 16-----19 * commit.c: 16-----19 - * sha1_name.c: 20 + * sha1-name.c: 20 * list-objects-filter.c: 21 * builtin/fsck.c: 0--3 * builtin/index-pack.c: 2021 diff --git a/sha1-name.c b/sha1-name.c new file mode 100644 index 0000000000..5b93bf8da3 --- /dev/null +++ b/sha1-name.c @@ -0,0 +1,1734 @@ +#include "cache.h" +#include "config.h" +#include "tag.h" +#include "commit.h" +#include "tree.h" +#include "blob.h" +#include "tree-walk.h" +#include "refs.h" +#include "remote.h" +#include "dir.h" +#include "sha1-array.h" +#include "packfile.h" +#include "object-store.h" +#include "repository.h" + +static int get_oid_oneline(const char *, struct object_id *, struct commit_list *); + +typedef int (*disambiguate_hint_fn)(const struct object_id *, void *); + +struct disambiguate_state { + int len; /* length of prefix in hex chars */ + char hex_pfx[GIT_MAX_HEXSZ + 1]; + struct object_id bin_pfx; + + disambiguate_hint_fn fn; + void *cb_data; + struct object_id candidate; + unsigned candidate_exists:1; + unsigned candidate_checked:1; + unsigned candidate_ok:1; + unsigned disambiguate_fn_used:1; + unsigned ambiguous:1; + unsigned always_call_fn:1; +}; + +static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) +{ + if (ds->always_call_fn) { + ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0; + return; + } + if (!ds->candidate_exists) { + /* this is the first candidate */ + oidcpy(&ds->candidate, current); + ds->candidate_exists = 1; + return; + } else if (!oidcmp(&ds->candidate, current)) { + /* the same as what we already have seen */ + return; + } + + if (!ds->fn) { + /* cannot disambiguate between ds->candidate and current */ + ds->ambiguous = 1; + return; + } + + if (!ds->candidate_checked) { + ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data); + ds->disambiguate_fn_used = 1; + ds->candidate_checked = 1; + } + + if (!ds->candidate_ok) { + /* discard the candidate; we know it does not satisfy fn */ + oidcpy(&ds->candidate, current); + ds->candidate_checked = 0; + return; + } + + /* if we reach this point, we know ds->candidate satisfies fn */ + if (ds->fn(current, ds->cb_data)) { + /* + * if both current and candidate satisfy fn, we cannot + * disambiguate. + */ + ds->candidate_ok = 0; + ds->ambiguous = 1; + } + + /* otherwise, current can be discarded and candidate is still good */ +} + +static int append_loose_object(const struct object_id *oid, const char *path, + void *data) +{ + oid_array_append(data, oid); + return 0; +} + +static int match_sha(unsigned, const unsigned char *, const unsigned char *); + +static void find_short_object_filename(struct disambiguate_state *ds) +{ + int subdir_nr = ds->bin_pfx.hash[0]; + struct alternate_object_database *alt; + static struct alternate_object_database *fakeent; + + if (!fakeent) { + /* + * Create a "fake" alternate object database that + * points to our own object database, to make it + * easier to get a temporary working space in + * alt->name/alt->base while iterating over the + * object databases including our own. + */ + fakeent = alloc_alt_odb(get_object_directory()); + } + fakeent->next = the_repository->objects->alt_odb_list; + + for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) { + int pos; + + if (!alt->loose_objects_subdir_seen[subdir_nr]) { + struct strbuf *buf = alt_scratch_buf(alt); + for_each_file_in_obj_subdir(subdir_nr, buf, + append_loose_object, + NULL, NULL, + &alt->loose_objects_cache); + alt->loose_objects_subdir_seen[subdir_nr] = 1; + } + + pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx); + if (pos < 0) + pos = -1 - pos; + while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) { + const struct object_id *oid; + oid = alt->loose_objects_cache.oid + pos; + if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash)) + break; + update_candidates(ds, oid); + pos++; + } + } +} + +static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b) +{ + do { + if (*a != *b) + return 0; + a++; + b++; + len -= 2; + } while (len > 1); + if (len) + if ((*a ^ *b) & 0xf0) + return 0; + return 1; +} + +static void unique_in_pack(struct packed_git *p, + struct disambiguate_state *ds) +{ + uint32_t num, i, first = 0; + const struct object_id *current = NULL; + + if (open_pack_index(p) || !p->num_objects) + return; + + num = p->num_objects; + bsearch_pack(&ds->bin_pfx, p, &first); + + /* + * At this point, "first" is the location of the lowest object + * with an object name that could match "bin_pfx". See if we have + * 0, 1 or more objects that actually match(es). + */ + for (i = first; i < num && !ds->ambiguous; i++) { + struct object_id oid; + current = nth_packed_object_oid(&oid, p, i); + if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash)) + break; + update_candidates(ds, current); + } +} + +static void find_short_packed_object(struct disambiguate_state *ds) +{ + struct packed_git *p; + + for (p = get_packed_git(the_repository); p && !ds->ambiguous; + p = p->next) + unique_in_pack(p, ds); +} + +#define SHORT_NAME_NOT_FOUND (-1) +#define SHORT_NAME_AMBIGUOUS (-2) + +static int finish_object_disambiguation(struct disambiguate_state *ds, + struct object_id *oid) +{ + if (ds->ambiguous) + return SHORT_NAME_AMBIGUOUS; + + if (!ds->candidate_exists) + return SHORT_NAME_NOT_FOUND; + + if (!ds->candidate_checked) + /* + * If this is the only candidate, there is no point + * calling the disambiguation hint callback. + * + * On the other hand, if the current candidate + * replaced an earlier candidate that did _not_ pass + * the disambiguation hint callback, then we do have + * more than one objects that match the short name + * given, so we should make sure this one matches; + * otherwise, if we discovered this one and the one + * that we previously discarded in the reverse order, + * we would end up showing different results in the + * same repository! + */ + ds->candidate_ok = (!ds->disambiguate_fn_used || + ds->fn(&ds->candidate, ds->cb_data)); + + if (!ds->candidate_ok) + return SHORT_NAME_AMBIGUOUS; + + oidcpy(oid, &ds->candidate); + return 0; +} + +static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused) +{ + int kind = oid_object_info(oid, NULL); + return kind == OBJ_COMMIT; +} + +static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused) +{ + struct object *obj; + int kind; + + kind = oid_object_info(oid, NULL); + if (kind == OBJ_COMMIT) + return 1; + if (kind != OBJ_TAG) + return 0; + + /* We need to do this the hard way... */ + obj = deref_tag(parse_object(oid), NULL, 0); + if (obj && obj->type == OBJ_COMMIT) + return 1; + return 0; +} + +static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused) +{ + int kind = oid_object_info(oid, NULL); + return kind == OBJ_TREE; +} + +static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused) +{ + struct object *obj; + int kind; + + kind = oid_object_info(oid, NULL); + if (kind == OBJ_TREE || kind == OBJ_COMMIT) + return 1; + if (kind != OBJ_TAG) + return 0; + + /* We need to do this the hard way... */ + obj = deref_tag(parse_object(oid), NULL, 0); + if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT)) + return 1; + return 0; +} + +static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused) +{ + int kind = oid_object_info(oid, NULL); + return kind == OBJ_BLOB; +} + +static disambiguate_hint_fn default_disambiguate_hint; + +int set_disambiguate_hint_config(const char *var, const char *value) +{ + static const struct { + const char *name; + disambiguate_hint_fn fn; + } hints[] = { + { "none", NULL }, + { "commit", disambiguate_commit_only }, + { "committish", disambiguate_committish_only }, + { "tree", disambiguate_tree_only }, + { "treeish", disambiguate_treeish_only }, + { "blob", disambiguate_blob_only } + }; + int i; + + if (!value) + return config_error_nonbool(var); + + for (i = 0; i < ARRAY_SIZE(hints); i++) { + if (!strcasecmp(value, hints[i].name)) { + default_disambiguate_hint = hints[i].fn; + return 0; + } + } + + return error("unknown hint type for '%s': %s", var, value); +} + +static int init_object_disambiguation(const char *name, int len, + struct disambiguate_state *ds) +{ + int i; + + if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ) + return -1; + + memset(ds, 0, sizeof(*ds)); + + for (i = 0; i < len ;i++) { + unsigned char c = name[i]; + unsigned char val; + if (c >= '0' && c <= '9') + val = c - '0'; + else if (c >= 'a' && c <= 'f') + val = c - 'a' + 10; + else if (c >= 'A' && c <='F') { + val = c - 'A' + 10; + c -= 'A' - 'a'; + } + else + return -1; + ds->hex_pfx[i] = c; + if (!(i & 1)) + val <<= 4; + ds->bin_pfx.hash[i >> 1] |= val; + } + + ds->len = len; + ds->hex_pfx[len] = '\0'; + prepare_alt_odb(the_repository); + return 0; +} + +static int show_ambiguous_object(const struct object_id *oid, void *data) +{ + const struct disambiguate_state *ds = data; + struct strbuf desc = STRBUF_INIT; + int type; + + + if (ds->fn && !ds->fn(oid, ds->cb_data)) + return 0; + + type = oid_object_info(oid, NULL); + if (type == OBJ_COMMIT) { + struct commit *commit = lookup_commit(oid); + if (commit) { + struct pretty_print_context pp = {0}; + pp.date_mode.type = DATE_SHORT; + format_commit_message(commit, " %ad - %s", &desc, &pp); + } + } else if (type == OBJ_TAG) { + struct tag *tag = lookup_tag(oid); + if (!parse_tag(tag) && tag->tag) + strbuf_addf(&desc, " %s", tag->tag); + } + + advise(" %s %s%s", + find_unique_abbrev(oid, DEFAULT_ABBREV), + type_name(type) ? type_name(type) : "unknown type", + desc.buf); + + strbuf_release(&desc); + return 0; +} + +static int get_short_oid(const char *name, int len, struct object_id *oid, + unsigned flags) +{ + int status; + struct disambiguate_state ds; + int quietly = !!(flags & GET_OID_QUIETLY); + + if (init_object_disambiguation(name, len, &ds) < 0) + return -1; + + if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS)) + die("BUG: multiple get_short_oid disambiguator flags"); + + if (flags & GET_OID_COMMIT) + ds.fn = disambiguate_commit_only; + else if (flags & GET_OID_COMMITTISH) + ds.fn = disambiguate_committish_only; + else if (flags & GET_OID_TREE) + ds.fn = disambiguate_tree_only; + else if (flags & GET_OID_TREEISH) + ds.fn = disambiguate_treeish_only; + else if (flags & GET_OID_BLOB) + ds.fn = disambiguate_blob_only; + else + ds.fn = default_disambiguate_hint; + + find_short_object_filename(&ds); + find_short_packed_object(&ds); + status = finish_object_disambiguation(&ds, oid); + + if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { + error(_("short SHA1 %s is ambiguous"), ds.hex_pfx); + + /* + * We may still have ambiguity if we simply saw a series of + * candidates that did not satisfy our hint function. In + * that case, we still want to show them, so disable the hint + * function entirely. + */ + if (!ds.ambiguous) + ds.fn = NULL; + + advise(_("The candidates are:")); + for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds); + } + + return status; +} + +static int collect_ambiguous(const struct object_id *oid, void *data) +{ + oid_array_append(data, oid); + return 0; +} + +int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) +{ + struct oid_array collect = OID_ARRAY_INIT; + struct disambiguate_state ds; + int ret; + + if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0) + return -1; + + ds.always_call_fn = 1; + ds.fn = collect_ambiguous; + ds.cb_data = &collect; + find_short_object_filename(&ds); + find_short_packed_object(&ds); + + ret = oid_array_for_each_unique(&collect, fn, cb_data); + oid_array_clear(&collect); + return ret; +} + +/* + * Return the slot of the most-significant bit set in "val". There are various + * ways to do this quickly with fls() or __builtin_clzl(), but speed is + * probably not a big deal here. + */ +static unsigned msb(unsigned long val) +{ + unsigned r = 0; + while (val >>= 1) + r++; + return r; +} + +struct min_abbrev_data { + unsigned int init_len; + unsigned int cur_len; + char *hex; + const struct object_id *oid; +}; + +static inline char get_hex_char_from_oid(const struct object_id *oid, + unsigned int pos) +{ + static const char hex[] = "0123456789abcdef"; + + if ((pos & 1) == 0) + return hex[oid->hash[pos >> 1] >> 4]; + else + return hex[oid->hash[pos >> 1] & 0xf]; +} + +static int extend_abbrev_len(const struct object_id *oid, void *cb_data) +{ + struct min_abbrev_data *mad = cb_data; + + unsigned int i = mad->init_len; + while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i)) + i++; + + if (i < GIT_MAX_RAWSZ && i >= mad->cur_len) + mad->cur_len = i + 1; + + return 0; +} + +static void find_abbrev_len_for_pack(struct packed_git *p, + struct min_abbrev_data *mad) +{ + int match = 0; + uint32_t num, first = 0; + struct object_id oid; + const struct object_id *mad_oid; + + if (open_pack_index(p) || !p->num_objects) + return; + + num = p->num_objects; + mad_oid = mad->oid; + match = bsearch_pack(mad_oid, p, &first); + + /* + * first is now the position in the packfile where we would insert + * mad->hash if it does not exist (or the position of mad->hash if + * it does exist). Hence, we consider a maximum of two objects + * nearby for the abbreviation length. + */ + mad->init_len = 0; + if (!match) { + if (nth_packed_object_oid(&oid, p, first)) + extend_abbrev_len(&oid, mad); + } else if (first < num - 1) { + if (nth_packed_object_oid(&oid, p, first + 1)) + extend_abbrev_len(&oid, mad); + } + if (first > 0) { + if (nth_packed_object_oid(&oid, p, first - 1)) + extend_abbrev_len(&oid, mad); + } + mad->init_len = mad->cur_len; +} + +static void find_abbrev_len_packed(struct min_abbrev_data *mad) +{ + struct packed_git *p; + + for (p = get_packed_git(the_repository); p; p = p->next) + find_abbrev_len_for_pack(p, mad); +} + +int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len) +{ + struct disambiguate_state ds; + struct min_abbrev_data mad; + struct object_id oid_ret; + if (len < 0) { + unsigned long count = approximate_object_count(); + /* + * Add one because the MSB only tells us the highest bit set, + * not including the value of all the _other_ bits (so "15" + * is only one off of 2^4, but the MSB is the 3rd bit. + */ + len = msb(count) + 1; + /* + * We now know we have on the order of 2^len objects, which + * expects a collision at 2^(len/2). But we also care about hex + * chars, not bits, and there are 4 bits per hex. So all + * together we need to divide by 2 and round up. + */ + len = DIV_ROUND_UP(len, 2); + /* + * For very small repos, we stick with our regular fallback. + */ + if (len < FALLBACK_DEFAULT_ABBREV) + len = FALLBACK_DEFAULT_ABBREV; + } + + oid_to_hex_r(hex, oid); + if (len == GIT_SHA1_HEXSZ || !len) + return GIT_SHA1_HEXSZ; + + mad.init_len = len; + mad.cur_len = len; + mad.hex = hex; + mad.oid = oid; + + find_abbrev_len_packed(&mad); + + if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0) + return -1; + + ds.fn = extend_abbrev_len; + ds.always_call_fn = 1; + ds.cb_data = (void *)&mad; + + find_short_object_filename(&ds); + (void)finish_object_disambiguation(&ds, &oid_ret); + + hex[mad.cur_len] = 0; + return mad.cur_len; +} + +const char *find_unique_abbrev(const struct object_id *oid, int len) +{ + static int bufno; + static char hexbuffer[4][GIT_MAX_HEXSZ + 1]; + char *hex = hexbuffer[bufno]; + bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); + find_unique_abbrev_r(hex, oid, len); + return hex; +} + +static int ambiguous_path(const char *path, int len) +{ + int slash = 1; + int cnt; + + for (cnt = 0; cnt < len; cnt++) { + switch (*path++) { + case '\0': + break; + case '/': + if (slash) + break; + slash = 1; + continue; + case '.': + continue; + default: + slash = 0; + continue; + } + break; + } + return slash; +} + +static inline int at_mark(const char *string, int len, + const char **suffix, int nr) +{ + int i; + + for (i = 0; i < nr; i++) { + int suffix_len = strlen(suffix[i]); + if (suffix_len <= len + && !strncasecmp(string, suffix[i], suffix_len)) + return suffix_len; + } + return 0; +} + +static inline int upstream_mark(const char *string, int len) +{ + const char *suffix[] = { "@{upstream}", "@{u}" }; + return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); +} + +static inline int push_mark(const char *string, int len) +{ + const char *suffix[] = { "@{push}" }; + return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); +} + +static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags); +static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf); + +static int get_oid_basic(const char *str, int len, struct object_id *oid, + unsigned int flags) +{ + static const char *warn_msg = "refname '%.*s' is ambiguous."; + static const char *object_name_msg = N_( + "Git normally never creates a ref that ends with 40 hex characters\n" + "because it will be ignored when you just specify 40-hex. These refs\n" + "may be created by mistake. For example,\n" + "\n" + " git checkout -b $br $(git rev-parse ...)\n" + "\n" + "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n" + "examine these refs and maybe delete them. Turn this message off by\n" + "running \"git config advice.objectNameWarning false\""); + struct object_id tmp_oid; + char *real_ref = NULL; + int refs_found = 0; + int at, reflog_len, nth_prior = 0; + + if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) { + if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) { + refs_found = dwim_ref(str, len, &tmp_oid, &real_ref); + if (refs_found > 0) { + warning(warn_msg, len, str); + if (advice_object_name_warning) + fprintf(stderr, "%s\n", _(object_name_msg)); + } + free(real_ref); + } + return 0; + } + + /* basic@{time or number or -number} format to query ref-log */ + reflog_len = at = 0; + if (len && str[len-1] == '}') { + for (at = len-4; at >= 0; at--) { + if (str[at] == '@' && str[at+1] == '{') { + if (str[at+2] == '-') { + if (at != 0) + /* @{-N} not at start */ + return -1; + nth_prior = 1; + continue; + } + if (!upstream_mark(str + at, len - at) && + !push_mark(str + at, len - at)) { + reflog_len = (len-1) - (at+2); + len = at; + } + break; + } + } + } + + /* Accept only unambiguous ref paths. */ + if (len && ambiguous_path(str, len)) + return -1; + + if (nth_prior) { + struct strbuf buf = STRBUF_INIT; + int detached; + + if (interpret_nth_prior_checkout(str, len, &buf) > 0) { + detached = (buf.len == GIT_SHA1_HEXSZ && !get_oid_hex(buf.buf, oid)); + strbuf_release(&buf); + if (detached) + return 0; + } + } + + if (!len && reflog_len) + /* allow "@{...}" to mean the current branch reflog */ + refs_found = dwim_ref("HEAD", 4, oid, &real_ref); + else if (reflog_len) + refs_found = dwim_log(str, len, oid, &real_ref); + else + refs_found = dwim_ref(str, len, oid, &real_ref); + + if (!refs_found) + return -1; + + if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) && + (refs_found > 1 || + !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY))) + warning(warn_msg, len, str); + + if (reflog_len) { + int nth, i; + timestamp_t at_time; + timestamp_t co_time; + int co_tz, co_cnt; + + /* Is it asking for N-th entry, or approxidate? */ + for (i = nth = 0; 0 <= nth && i < reflog_len; i++) { + char ch = str[at+2+i]; + if ('0' <= ch && ch <= '9') + nth = nth * 10 + ch - '0'; + else + nth = -1; + } + if (100000000 <= nth) { + at_time = nth; + nth = -1; + } else if (0 <= nth) + at_time = 0; + else { + int errors = 0; + char *tmp = xstrndup(str + at + 2, reflog_len); + at_time = approxidate_careful(tmp, &errors); + free(tmp); + if (errors) { + free(real_ref); + return -1; + } + } + if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL, + &co_time, &co_tz, &co_cnt)) { + if (!len) { + if (starts_with(real_ref, "refs/heads/")) { + str = real_ref + 11; + len = strlen(real_ref + 11); + } else { + /* detached HEAD */ + str = "HEAD"; + len = 4; + } + } + if (at_time) { + if (!(flags & GET_OID_QUIETLY)) { + warning("Log for '%.*s' only goes " + "back to %s.", len, str, + show_date(co_time, co_tz, DATE_MODE(RFC2822))); + } + } else { + if (flags & GET_OID_QUIETLY) { + exit(128); + } + die("Log for '%.*s' only has %d entries.", + len, str, co_cnt); + } + } + } + + free(real_ref); + return 0; +} + +static int get_parent(const char *name, int len, + struct object_id *result, int idx) +{ + struct object_id oid; + int ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH); + struct commit *commit; + struct commit_list *p; + + if (ret) + return ret; + commit = lookup_commit_reference(&oid); + if (parse_commit(commit)) + return -1; + if (!idx) { + oidcpy(result, &commit->object.oid); + return 0; + } + p = commit->parents; + while (p) { + if (!--idx) { + oidcpy(result, &p->item->object.oid); + return 0; + } + p = p->next; + } + return -1; +} + +static int get_nth_ancestor(const char *name, int len, + struct object_id *result, int generation) +{ + struct object_id oid; + struct commit *commit; + int ret; + + ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH); + if (ret) + return ret; + commit = lookup_commit_reference(&oid); + if (!commit) + return -1; + + while (generation--) { + if (parse_commit(commit) || !commit->parents) + return -1; + commit = commit->parents->item; + } + oidcpy(result, &commit->object.oid); + return 0; +} + +struct object *peel_to_type(const char *name, int namelen, + struct object *o, enum object_type expected_type) +{ + if (name && !namelen) + namelen = strlen(name); + while (1) { + if (!o || (!o->parsed && !parse_object(&o->oid))) + return NULL; + if (expected_type == OBJ_ANY || o->type == expected_type) + return o; + if (o->type == OBJ_TAG) + o = ((struct tag*) o)->tagged; + else if (o->type == OBJ_COMMIT) + o = &(((struct commit *) o)->tree->object); + else { + if (name) + error("%.*s: expected %s type, but the object " + "dereferences to %s type", + namelen, name, type_name(expected_type), + type_name(o->type)); + return NULL; + } + } +} + +static int peel_onion(const char *name, int len, struct object_id *oid, + unsigned lookup_flags) +{ + struct object_id outer; + const char *sp; + unsigned int expected_type = 0; + struct object *o; + + /* + * "ref^{type}" dereferences ref repeatedly until you cannot + * dereference anymore, or you get an object of given type, + * whichever comes first. "ref^{}" means just dereference + * tags until you get a non-tag. "ref^0" is a shorthand for + * "ref^{commit}". "commit^{tree}" could be used to find the + * top-level tree of the given commit. + */ + if (len < 4 || name[len-1] != '}') + return -1; + + for (sp = name + len - 1; name <= sp; sp--) { + int ch = *sp; + if (ch == '{' && name < sp && sp[-1] == '^') + break; + } + if (sp <= name) + return -1; + + sp++; /* beginning of type name, or closing brace for empty */ + if (starts_with(sp, "commit}")) + expected_type = OBJ_COMMIT; + else if (starts_with(sp, "tag}")) + expected_type = OBJ_TAG; + else if (starts_with(sp, "tree}")) + expected_type = OBJ_TREE; + else if (starts_with(sp, "blob}")) + expected_type = OBJ_BLOB; + else if (starts_with(sp, "object}")) + expected_type = OBJ_ANY; + else if (sp[0] == '}') + expected_type = OBJ_NONE; + else if (sp[0] == '/') + expected_type = OBJ_COMMIT; + else + return -1; + + lookup_flags &= ~GET_OID_DISAMBIGUATORS; + if (expected_type == OBJ_COMMIT) + lookup_flags |= GET_OID_COMMITTISH; + else if (expected_type == OBJ_TREE) + lookup_flags |= GET_OID_TREEISH; + + if (get_oid_1(name, sp - name - 2, &outer, lookup_flags)) + return -1; + + o = parse_object(&outer); + if (!o) + return -1; + if (!expected_type) { + o = deref_tag(o, name, sp - name - 2); + if (!o || (!o->parsed && !parse_object(&o->oid))) + return -1; + oidcpy(oid, &o->oid); + return 0; + } + + /* + * At this point, the syntax look correct, so + * if we do not get the needed object, we should + * barf. + */ + o = peel_to_type(name, len, o, expected_type); + if (!o) + return -1; + + oidcpy(oid, &o->oid); + if (sp[0] == '/') { + /* "$commit^{/foo}" */ + char *prefix; + int ret; + struct commit_list *list = NULL; + + /* + * $commit^{/}. Some regex implementation may reject. + * We don't need regex anyway. '' pattern always matches. + */ + if (sp[1] == '}') + return 0; + + prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); + commit_list_insert((struct commit *)o, &list); + ret = get_oid_oneline(prefix, oid, list); + free(prefix); + return ret; + } + return 0; +} + +static int get_describe_name(const char *name, int len, struct object_id *oid) +{ + const char *cp; + unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT; + + for (cp = name + len - 1; name + 2 <= cp; cp--) { + char ch = *cp; + if (!isxdigit(ch)) { + /* We must be looking at g in "SOMETHING-g" + * for it to be describe output. + */ + if (ch == 'g' && cp[-1] == '-') { + cp++; + len -= cp - name; + return get_short_oid(cp, len, oid, flags); + } + } + } + return -1; +} + +static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags) +{ + int ret, has_suffix; + const char *cp; + + /* + * "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1". + */ + has_suffix = 0; + for (cp = name + len - 1; name <= cp; cp--) { + int ch = *cp; + if ('0' <= ch && ch <= '9') + continue; + if (ch == '~' || ch == '^') + has_suffix = ch; + break; + } + + if (has_suffix) { + int num = 0; + int len1 = cp - name; + cp++; + while (cp < name + len) + num = num * 10 + *cp++ - '0'; + if (!num && len1 == len - 1) + num = 1; + if (has_suffix == '^') + return get_parent(name, len1, oid, num); + /* else if (has_suffix == '~') -- goes without saying */ + return get_nth_ancestor(name, len1, oid, num); + } + + ret = peel_onion(name, len, oid, lookup_flags); + if (!ret) + return 0; + + ret = get_oid_basic(name, len, oid, lookup_flags); + if (!ret) + return 0; + + /* It could be describe output that is "SOMETHING-gXXXX" */ + ret = get_describe_name(name, len, oid); + if (!ret) + return 0; + + return get_short_oid(name, len, oid, lookup_flags); +} + +/* + * This interprets names like ':/Initial revision of "git"' by searching + * through history and returning the first commit whose message starts + * the given regular expression. + * + * For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'. + * + * For a literal '!' character at the beginning of a pattern, you have to repeat + * that, like: ':/!!foo' + * + * For future extension, all other sequences beginning with ':/!' are reserved. + */ + +/* Remember to update object flag allocation in object.h */ +#define ONELINE_SEEN (1u<<20) + +static int handle_one_ref(const char *path, const struct object_id *oid, + int flag, void *cb_data) +{ + struct commit_list **list = cb_data; + struct object *object = parse_object(oid); + if (!object) + return 0; + if (object->type == OBJ_TAG) { + object = deref_tag(object, path, strlen(path)); + if (!object) + return 0; + } + if (object->type != OBJ_COMMIT) + return 0; + commit_list_insert((struct commit *)object, list); + return 0; +} + +static int get_oid_oneline(const char *prefix, struct object_id *oid, + struct commit_list *list) +{ + struct commit_list *backup = NULL, *l; + int found = 0; + int negative = 0; + regex_t regex; + + if (prefix[0] == '!') { + prefix++; + + if (prefix[0] == '-') { + prefix++; + negative = 1; + } else if (prefix[0] != '!') { + return -1; + } + } + + if (regcomp(®ex, prefix, REG_EXTENDED)) + return -1; + + for (l = list; l; l = l->next) { + l->item->object.flags |= ONELINE_SEEN; + commit_list_insert(l->item, &backup); + } + while (list) { + const char *p, *buf; + struct commit *commit; + int matches; + + commit = pop_most_recent_commit(&list, ONELINE_SEEN); + if (!parse_object(&commit->object.oid)) + continue; + buf = get_commit_buffer(commit, NULL); + p = strstr(buf, "\n\n"); + matches = negative ^ (p && !regexec(®ex, p + 2, 0, NULL, 0)); + unuse_commit_buffer(commit, buf); + + if (matches) { + oidcpy(oid, &commit->object.oid); + found = 1; + break; + } + } + regfree(®ex); + free_commit_list(list); + for (l = backup; l; l = l->next) + clear_commit_marks(l->item, ONELINE_SEEN); + free_commit_list(backup); + return found ? 0 : -1; +} + +struct grab_nth_branch_switch_cbdata { + int remaining; + struct strbuf buf; +}; + +static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, + const char *message, void *cb_data) +{ + struct grab_nth_branch_switch_cbdata *cb = cb_data; + const char *match = NULL, *target = NULL; + size_t len; + + if (skip_prefix(message, "checkout: moving from ", &match)) + target = strstr(match, " to "); + + if (!match || !target) + return 0; + if (--(cb->remaining) == 0) { + len = target - match; + strbuf_reset(&cb->buf); + strbuf_add(&cb->buf, match, len); + return 1; /* we are done */ + } + return 0; +} + +/* + * Parse @{-N} syntax, return the number of characters parsed + * if successful; otherwise signal an error with negative value. + */ +static int interpret_nth_prior_checkout(const char *name, int namelen, + struct strbuf *buf) +{ + long nth; + int retval; + struct grab_nth_branch_switch_cbdata cb; + const char *brace; + char *num_end; + + if (namelen < 4) + return -1; + if (name[0] != '@' || name[1] != '{' || name[2] != '-') + return -1; + brace = memchr(name, '}', namelen); + if (!brace) + return -1; + nth = strtol(name + 3, &num_end, 10); + if (num_end != brace) + return -1; + if (nth <= 0) + return -1; + cb.remaining = nth; + strbuf_init(&cb.buf, 20); + + retval = 0; + if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) { + strbuf_reset(buf); + strbuf_addbuf(buf, &cb.buf); + retval = brace - name + 1; + } + + strbuf_release(&cb.buf); + return retval; +} + +int get_oid_mb(const char *name, struct object_id *oid) +{ + struct commit *one, *two; + struct commit_list *mbs; + struct object_id oid_tmp; + const char *dots; + int st; + + dots = strstr(name, "..."); + if (!dots) + return get_oid(name, oid); + if (dots == name) + st = get_oid("HEAD", &oid_tmp); + else { + struct strbuf sb; + strbuf_init(&sb, dots - name); + strbuf_add(&sb, name, dots - name); + st = get_oid_committish(sb.buf, &oid_tmp); + strbuf_release(&sb); + } + if (st) + return st; + one = lookup_commit_reference_gently(&oid_tmp, 0); + if (!one) + return -1; + + if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp)) + return -1; + two = lookup_commit_reference_gently(&oid_tmp, 0); + if (!two) + return -1; + mbs = get_merge_bases(one, two); + if (!mbs || mbs->next) + st = -1; + else { + st = 0; + oidcpy(oid, &mbs->item->object.oid); + } + free_commit_list(mbs); + return st; +} + +/* parse @something syntax, when 'something' is not {.*} */ +static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf) +{ + const char *next; + + if (len || name[1] == '{') + return -1; + + /* make sure it's a single @, or @@{.*}, not @foo */ + next = memchr(name + len + 1, '@', namelen - len - 1); + if (next && next[1] != '{') + return -1; + if (!next) + next = name + namelen; + if (next != name + 1) + return -1; + + strbuf_reset(buf); + strbuf_add(buf, "HEAD", 4); + return 1; +} + +static int reinterpret(const char *name, int namelen, int len, + struct strbuf *buf, unsigned allowed) +{ + /* we have extra data, which might need further processing */ + struct strbuf tmp = STRBUF_INIT; + int used = buf->len; + int ret; + + strbuf_add(buf, name + len, namelen - len); + ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); + /* that data was not interpreted, remove our cruft */ + if (ret < 0) { + strbuf_setlen(buf, used); + return len; + } + strbuf_reset(buf); + strbuf_addbuf(buf, &tmp); + strbuf_release(&tmp); + /* tweak for size of {-N} versus expanded ref name */ + return ret - used + len; +} + +static void set_shortened_ref(struct strbuf *buf, const char *ref) +{ + char *s = shorten_unambiguous_ref(ref, 0); + strbuf_reset(buf); + strbuf_addstr(buf, s); + free(s); +} + +static int branch_interpret_allowed(const char *refname, unsigned allowed) +{ + if (!allowed) + return 1; + + if ((allowed & INTERPRET_BRANCH_LOCAL) && + starts_with(refname, "refs/heads/")) + return 1; + if ((allowed & INTERPRET_BRANCH_REMOTE) && + starts_with(refname, "refs/remotes/")) + return 1; + + return 0; +} + +static int interpret_branch_mark(const char *name, int namelen, + int at, struct strbuf *buf, + int (*get_mark)(const char *, int), + const char *(*get_data)(struct branch *, + struct strbuf *), + unsigned allowed) +{ + int len; + struct branch *branch; + struct strbuf err = STRBUF_INIT; + const char *value; + + len = get_mark(name + at, namelen - at); + if (!len) + return -1; + + if (memchr(name, ':', at)) + return -1; + + if (at) { + char *name_str = xmemdupz(name, at); + branch = branch_get(name_str); + free(name_str); + } else + branch = branch_get(NULL); + + value = get_data(branch, &err); + if (!value) + die("%s", err.buf); + + if (!branch_interpret_allowed(value, allowed)) + return -1; + + set_shortened_ref(buf, value); + return len + at; +} + +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, + unsigned allowed) +{ + char *at; + const char *start; + int len; + + if (!namelen) + namelen = strlen(name); + + if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { + len = interpret_nth_prior_checkout(name, namelen, buf); + if (!len) { + return len; /* syntax Ok, not enough switches */ + } else if (len > 0) { + if (len == namelen) + return len; /* consumed all */ + else + return reinterpret(name, namelen, len, buf, allowed); + } + } + + for (start = name; + (at = memchr(start, '@', namelen - (start - name))); + start = at + 1) { + + if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { + len = interpret_empty_at(name, namelen, at - name, buf); + if (len > 0) + return reinterpret(name, namelen, len, buf, + allowed); + } + + len = interpret_branch_mark(name, namelen, at - name, buf, + upstream_mark, branch_get_upstream, + allowed); + if (len > 0) + return len; + + len = interpret_branch_mark(name, namelen, at - name, buf, + push_mark, branch_get_push, + allowed); + if (len > 0) + return len; + } + + return -1; +} + +void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) +{ + int len = strlen(name); + int used = interpret_branch_name(name, len, sb, allowed); + + if (used < 0) + used = 0; + strbuf_add(sb, name + used, len - used); +} + +int strbuf_check_branch_ref(struct strbuf *sb, const char *name) +{ + if (startup_info->have_repository) + strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); + else + strbuf_addstr(sb, name); + + /* + * This splice must be done even if we end up rejecting the + * name; builtin/branch.c::copy_or_rename_branch() still wants + * to see what the name expanded to so that "branch -m" can be + * used as a tool to correct earlier mistakes. + */ + strbuf_splice(sb, 0, 0, "refs/heads/", 11); + + if (*name == '-' || + !strcmp(sb->buf, "refs/heads/HEAD")) + return -1; + + return check_refname_format(sb->buf, 0); +} + +/* + * This is like "get_oid_basic()", except it allows "object ID expressions", + * notably "xyz^" for "parent of xyz" + */ +int get_oid(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, 0, oid, &unused); +} + + +/* + * Many callers know that the user meant to name a commit-ish by + * syntactical positions where the object name appears. Calling this + * function allows the machinery to disambiguate shorter-than-unique + * abbreviated object names between commit-ish and others. + * + * Note that this does NOT error out when the named object is not a + * commit-ish. It is merely to give a hint to the disambiguation + * machinery. + */ +int get_oid_committish(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, GET_OID_COMMITTISH, + oid, &unused); +} + +int get_oid_treeish(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, GET_OID_TREEISH, + oid, &unused); +} + +int get_oid_commit(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, GET_OID_COMMIT, + oid, &unused); +} + +int get_oid_tree(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, GET_OID_TREE, + oid, &unused); +} + +int get_oid_blob(const char *name, struct object_id *oid) +{ + struct object_context unused; + return get_oid_with_context(name, GET_OID_BLOB, + oid, &unused); +} + +/* Must be called only when object_name:filename doesn't exist. */ +static void diagnose_invalid_oid_path(const char *prefix, + const char *filename, + const struct object_id *tree_oid, + const char *object_name, + int object_name_len) +{ + struct object_id oid; + unsigned mode; + + if (!prefix) + prefix = ""; + + if (file_exists(filename)) + die("Path '%s' exists on disk, but not in '%.*s'.", + filename, object_name_len, object_name); + if (is_missing_file_error(errno)) { + char *fullname = xstrfmt("%s%s", prefix, filename); + + if (!get_tree_entry(tree_oid, fullname, &oid, &mode)) { + die("Path '%s' exists, but not '%s'.\n" + "Did you mean '%.*s:%s' aka '%.*s:./%s'?", + fullname, + filename, + object_name_len, object_name, + fullname, + object_name_len, object_name, + filename); + } + die("Path '%s' does not exist in '%.*s'", + filename, object_name_len, object_name); + } +} + +/* Must be called only when :stage:filename doesn't exist. */ +static void diagnose_invalid_index_path(int stage, + const char *prefix, + const char *filename) +{ + const struct cache_entry *ce; + int pos; + unsigned namelen = strlen(filename); + struct strbuf fullname = STRBUF_INIT; + + if (!prefix) + prefix = ""; + + /* Wrong stage number? */ + pos = cache_name_pos(filename, namelen); + if (pos < 0) + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, filename, namelen)) + die("Path '%s' is in the index, but not at stage %d.\n" + "Did you mean ':%d:%s'?", + filename, stage, + ce_stage(ce), filename); + } + + /* Confusion between relative and absolute filenames? */ + strbuf_addstr(&fullname, prefix); + strbuf_addstr(&fullname, filename); + pos = cache_name_pos(fullname.buf, fullname.len); + if (pos < 0) + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == fullname.len && + !memcmp(ce->name, fullname.buf, fullname.len)) + die("Path '%s' is in the index, but not '%s'.\n" + "Did you mean ':%d:%s' aka ':%d:./%s'?", + fullname.buf, filename, + ce_stage(ce), fullname.buf, + ce_stage(ce), filename); + } + + if (file_exists(filename)) + die("Path '%s' exists on disk, but not in the index.", filename); + if (is_missing_file_error(errno)) + die("Path '%s' does not exist (neither on disk nor in the index).", + filename); + + strbuf_release(&fullname); +} + + +static char *resolve_relative_path(const char *rel) +{ + if (!starts_with(rel, "./") && !starts_with(rel, "../")) + return NULL; + + if (!is_inside_work_tree()) + die("relative path syntax can't be used outside working tree."); + + /* die() inside prefix_path() if resolved path is outside worktree */ + return prefix_path(startup_info->prefix, + startup_info->prefix ? strlen(startup_info->prefix) : 0, + rel); +} + +static int get_oid_with_context_1(const char *name, + unsigned flags, + const char *prefix, + struct object_id *oid, + struct object_context *oc) +{ + int ret, bracket_depth; + int namelen = strlen(name); + const char *cp; + int only_to_die = flags & GET_OID_ONLY_TO_DIE; + + if (only_to_die) + flags |= GET_OID_QUIETLY; + + memset(oc, 0, sizeof(*oc)); + oc->mode = S_IFINVALID; + strbuf_init(&oc->symlink_path, 0); + ret = get_oid_1(name, namelen, oid, flags); + if (!ret) + return ret; + /* + * sha1:path --> object name of path in ent sha1 + * :path -> object name of absolute path in index + * :./path -> object name of path relative to cwd in index + * :[0-3]:path -> object name of path in index at stage + * :/foo -> recent commit matching foo + */ + if (name[0] == ':') { + int stage = 0; + const struct cache_entry *ce; + char *new_path = NULL; + int pos; + if (!only_to_die && namelen > 2 && name[1] == '/') { + struct commit_list *list = NULL; + + for_each_ref(handle_one_ref, &list); + commit_list_sort_by_date(&list); + return get_oid_oneline(name + 2, oid, list); + } + if (namelen < 3 || + name[2] != ':' || + name[1] < '0' || '3' < name[1]) + cp = name + 1; + else { + stage = name[1] - '0'; + cp = name + 3; + } + new_path = resolve_relative_path(cp); + if (!new_path) { + namelen = namelen - (cp - name); + } else { + cp = new_path; + namelen = strlen(cp); + } + + if (flags & GET_OID_RECORD_PATH) + oc->path = xstrdup(cp); + + if (!active_cache) + read_cache(); + pos = cache_name_pos(cp, namelen); + if (pos < 0) + pos = -pos - 1; + while (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) != namelen || + memcmp(ce->name, cp, namelen)) + break; + if (ce_stage(ce) == stage) { + oidcpy(oid, &ce->oid); + oc->mode = ce->ce_mode; + free(new_path); + return 0; + } + pos++; + } + if (only_to_die && name[1] && name[1] != '/') + diagnose_invalid_index_path(stage, prefix, cp); + free(new_path); + return -1; + } + for (cp = name, bracket_depth = 0; *cp; cp++) { + if (*cp == '{') + bracket_depth++; + else if (bracket_depth && *cp == '}') + bracket_depth--; + else if (!bracket_depth && *cp == ':') + break; + } + if (*cp == ':') { + struct object_id tree_oid; + int len = cp - name; + unsigned sub_flags = flags; + + sub_flags &= ~GET_OID_DISAMBIGUATORS; + sub_flags |= GET_OID_TREEISH; + + if (!get_oid_1(name, len, &tree_oid, sub_flags)) { + const char *filename = cp+1; + char *new_filename = NULL; + + new_filename = resolve_relative_path(filename); + if (new_filename) + filename = new_filename; + if (flags & GET_OID_FOLLOW_SYMLINKS) { + ret = get_tree_entry_follow_symlinks(tree_oid.hash, + filename, oid->hash, &oc->symlink_path, + &oc->mode); + } else { + ret = get_tree_entry(&tree_oid, filename, oid, + &oc->mode); + if (ret && only_to_die) { + diagnose_invalid_oid_path(prefix, + filename, + &tree_oid, + name, len); + } + } + hashcpy(oc->tree, tree_oid.hash); + if (flags & GET_OID_RECORD_PATH) + oc->path = xstrdup(filename); + + free(new_filename); + return ret; + } else { + if (only_to_die) + die("Invalid object name '%.*s'.", len, name); + } + } + return ret; +} + +/* + * Call this function when you know "name" given by the end user must + * name an object but it doesn't; the function _may_ die with a better + * diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not + * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case + * you have a chance to diagnose the error further. + */ +void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) +{ + struct object_context oc; + struct object_id oid; + get_oid_with_context_1(name, GET_OID_ONLY_TO_DIE, prefix, &oid, &oc); +} + +int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc) +{ + if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE) + die("BUG: incompatible flags for get_sha1_with_context"); + return get_oid_with_context_1(str, flags, NULL, oid, oc); +} diff --git a/sha1_name.c b/sha1_name.c deleted file mode 100644 index 5b93bf8da3..0000000000 --- a/sha1_name.c +++ /dev/null @@ -1,1734 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "tag.h" -#include "commit.h" -#include "tree.h" -#include "blob.h" -#include "tree-walk.h" -#include "refs.h" -#include "remote.h" -#include "dir.h" -#include "sha1-array.h" -#include "packfile.h" -#include "object-store.h" -#include "repository.h" - -static int get_oid_oneline(const char *, struct object_id *, struct commit_list *); - -typedef int (*disambiguate_hint_fn)(const struct object_id *, void *); - -struct disambiguate_state { - int len; /* length of prefix in hex chars */ - char hex_pfx[GIT_MAX_HEXSZ + 1]; - struct object_id bin_pfx; - - disambiguate_hint_fn fn; - void *cb_data; - struct object_id candidate; - unsigned candidate_exists:1; - unsigned candidate_checked:1; - unsigned candidate_ok:1; - unsigned disambiguate_fn_used:1; - unsigned ambiguous:1; - unsigned always_call_fn:1; -}; - -static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) -{ - if (ds->always_call_fn) { - ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0; - return; - } - if (!ds->candidate_exists) { - /* this is the first candidate */ - oidcpy(&ds->candidate, current); - ds->candidate_exists = 1; - return; - } else if (!oidcmp(&ds->candidate, current)) { - /* the same as what we already have seen */ - return; - } - - if (!ds->fn) { - /* cannot disambiguate between ds->candidate and current */ - ds->ambiguous = 1; - return; - } - - if (!ds->candidate_checked) { - ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data); - ds->disambiguate_fn_used = 1; - ds->candidate_checked = 1; - } - - if (!ds->candidate_ok) { - /* discard the candidate; we know it does not satisfy fn */ - oidcpy(&ds->candidate, current); - ds->candidate_checked = 0; - return; - } - - /* if we reach this point, we know ds->candidate satisfies fn */ - if (ds->fn(current, ds->cb_data)) { - /* - * if both current and candidate satisfy fn, we cannot - * disambiguate. - */ - ds->candidate_ok = 0; - ds->ambiguous = 1; - } - - /* otherwise, current can be discarded and candidate is still good */ -} - -static int append_loose_object(const struct object_id *oid, const char *path, - void *data) -{ - oid_array_append(data, oid); - return 0; -} - -static int match_sha(unsigned, const unsigned char *, const unsigned char *); - -static void find_short_object_filename(struct disambiguate_state *ds) -{ - int subdir_nr = ds->bin_pfx.hash[0]; - struct alternate_object_database *alt; - static struct alternate_object_database *fakeent; - - if (!fakeent) { - /* - * Create a "fake" alternate object database that - * points to our own object database, to make it - * easier to get a temporary working space in - * alt->name/alt->base while iterating over the - * object databases including our own. - */ - fakeent = alloc_alt_odb(get_object_directory()); - } - fakeent->next = the_repository->objects->alt_odb_list; - - for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) { - int pos; - - if (!alt->loose_objects_subdir_seen[subdir_nr]) { - struct strbuf *buf = alt_scratch_buf(alt); - for_each_file_in_obj_subdir(subdir_nr, buf, - append_loose_object, - NULL, NULL, - &alt->loose_objects_cache); - alt->loose_objects_subdir_seen[subdir_nr] = 1; - } - - pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx); - if (pos < 0) - pos = -1 - pos; - while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) { - const struct object_id *oid; - oid = alt->loose_objects_cache.oid + pos; - if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash)) - break; - update_candidates(ds, oid); - pos++; - } - } -} - -static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b) -{ - do { - if (*a != *b) - return 0; - a++; - b++; - len -= 2; - } while (len > 1); - if (len) - if ((*a ^ *b) & 0xf0) - return 0; - return 1; -} - -static void unique_in_pack(struct packed_git *p, - struct disambiguate_state *ds) -{ - uint32_t num, i, first = 0; - const struct object_id *current = NULL; - - if (open_pack_index(p) || !p->num_objects) - return; - - num = p->num_objects; - bsearch_pack(&ds->bin_pfx, p, &first); - - /* - * At this point, "first" is the location of the lowest object - * with an object name that could match "bin_pfx". See if we have - * 0, 1 or more objects that actually match(es). - */ - for (i = first; i < num && !ds->ambiguous; i++) { - struct object_id oid; - current = nth_packed_object_oid(&oid, p, i); - if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash)) - break; - update_candidates(ds, current); - } -} - -static void find_short_packed_object(struct disambiguate_state *ds) -{ - struct packed_git *p; - - for (p = get_packed_git(the_repository); p && !ds->ambiguous; - p = p->next) - unique_in_pack(p, ds); -} - -#define SHORT_NAME_NOT_FOUND (-1) -#define SHORT_NAME_AMBIGUOUS (-2) - -static int finish_object_disambiguation(struct disambiguate_state *ds, - struct object_id *oid) -{ - if (ds->ambiguous) - return SHORT_NAME_AMBIGUOUS; - - if (!ds->candidate_exists) - return SHORT_NAME_NOT_FOUND; - - if (!ds->candidate_checked) - /* - * If this is the only candidate, there is no point - * calling the disambiguation hint callback. - * - * On the other hand, if the current candidate - * replaced an earlier candidate that did _not_ pass - * the disambiguation hint callback, then we do have - * more than one objects that match the short name - * given, so we should make sure this one matches; - * otherwise, if we discovered this one and the one - * that we previously discarded in the reverse order, - * we would end up showing different results in the - * same repository! - */ - ds->candidate_ok = (!ds->disambiguate_fn_used || - ds->fn(&ds->candidate, ds->cb_data)); - - if (!ds->candidate_ok) - return SHORT_NAME_AMBIGUOUS; - - oidcpy(oid, &ds->candidate); - return 0; -} - -static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused) -{ - int kind = oid_object_info(oid, NULL); - return kind == OBJ_COMMIT; -} - -static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused) -{ - struct object *obj; - int kind; - - kind = oid_object_info(oid, NULL); - if (kind == OBJ_COMMIT) - return 1; - if (kind != OBJ_TAG) - return 0; - - /* We need to do this the hard way... */ - obj = deref_tag(parse_object(oid), NULL, 0); - if (obj && obj->type == OBJ_COMMIT) - return 1; - return 0; -} - -static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused) -{ - int kind = oid_object_info(oid, NULL); - return kind == OBJ_TREE; -} - -static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused) -{ - struct object *obj; - int kind; - - kind = oid_object_info(oid, NULL); - if (kind == OBJ_TREE || kind == OBJ_COMMIT) - return 1; - if (kind != OBJ_TAG) - return 0; - - /* We need to do this the hard way... */ - obj = deref_tag(parse_object(oid), NULL, 0); - if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT)) - return 1; - return 0; -} - -static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused) -{ - int kind = oid_object_info(oid, NULL); - return kind == OBJ_BLOB; -} - -static disambiguate_hint_fn default_disambiguate_hint; - -int set_disambiguate_hint_config(const char *var, const char *value) -{ - static const struct { - const char *name; - disambiguate_hint_fn fn; - } hints[] = { - { "none", NULL }, - { "commit", disambiguate_commit_only }, - { "committish", disambiguate_committish_only }, - { "tree", disambiguate_tree_only }, - { "treeish", disambiguate_treeish_only }, - { "blob", disambiguate_blob_only } - }; - int i; - - if (!value) - return config_error_nonbool(var); - - for (i = 0; i < ARRAY_SIZE(hints); i++) { - if (!strcasecmp(value, hints[i].name)) { - default_disambiguate_hint = hints[i].fn; - return 0; - } - } - - return error("unknown hint type for '%s': %s", var, value); -} - -static int init_object_disambiguation(const char *name, int len, - struct disambiguate_state *ds) -{ - int i; - - if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ) - return -1; - - memset(ds, 0, sizeof(*ds)); - - for (i = 0; i < len ;i++) { - unsigned char c = name[i]; - unsigned char val; - if (c >= '0' && c <= '9') - val = c - '0'; - else if (c >= 'a' && c <= 'f') - val = c - 'a' + 10; - else if (c >= 'A' && c <='F') { - val = c - 'A' + 10; - c -= 'A' - 'a'; - } - else - return -1; - ds->hex_pfx[i] = c; - if (!(i & 1)) - val <<= 4; - ds->bin_pfx.hash[i >> 1] |= val; - } - - ds->len = len; - ds->hex_pfx[len] = '\0'; - prepare_alt_odb(the_repository); - return 0; -} - -static int show_ambiguous_object(const struct object_id *oid, void *data) -{ - const struct disambiguate_state *ds = data; - struct strbuf desc = STRBUF_INIT; - int type; - - - if (ds->fn && !ds->fn(oid, ds->cb_data)) - return 0; - - type = oid_object_info(oid, NULL); - if (type == OBJ_COMMIT) { - struct commit *commit = lookup_commit(oid); - if (commit) { - struct pretty_print_context pp = {0}; - pp.date_mode.type = DATE_SHORT; - format_commit_message(commit, " %ad - %s", &desc, &pp); - } - } else if (type == OBJ_TAG) { - struct tag *tag = lookup_tag(oid); - if (!parse_tag(tag) && tag->tag) - strbuf_addf(&desc, " %s", tag->tag); - } - - advise(" %s %s%s", - find_unique_abbrev(oid, DEFAULT_ABBREV), - type_name(type) ? type_name(type) : "unknown type", - desc.buf); - - strbuf_release(&desc); - return 0; -} - -static int get_short_oid(const char *name, int len, struct object_id *oid, - unsigned flags) -{ - int status; - struct disambiguate_state ds; - int quietly = !!(flags & GET_OID_QUIETLY); - - if (init_object_disambiguation(name, len, &ds) < 0) - return -1; - - if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS)) - die("BUG: multiple get_short_oid disambiguator flags"); - - if (flags & GET_OID_COMMIT) - ds.fn = disambiguate_commit_only; - else if (flags & GET_OID_COMMITTISH) - ds.fn = disambiguate_committish_only; - else if (flags & GET_OID_TREE) - ds.fn = disambiguate_tree_only; - else if (flags & GET_OID_TREEISH) - ds.fn = disambiguate_treeish_only; - else if (flags & GET_OID_BLOB) - ds.fn = disambiguate_blob_only; - else - ds.fn = default_disambiguate_hint; - - find_short_object_filename(&ds); - find_short_packed_object(&ds); - status = finish_object_disambiguation(&ds, oid); - - if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { - error(_("short SHA1 %s is ambiguous"), ds.hex_pfx); - - /* - * We may still have ambiguity if we simply saw a series of - * candidates that did not satisfy our hint function. In - * that case, we still want to show them, so disable the hint - * function entirely. - */ - if (!ds.ambiguous) - ds.fn = NULL; - - advise(_("The candidates are:")); - for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds); - } - - return status; -} - -static int collect_ambiguous(const struct object_id *oid, void *data) -{ - oid_array_append(data, oid); - return 0; -} - -int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) -{ - struct oid_array collect = OID_ARRAY_INIT; - struct disambiguate_state ds; - int ret; - - if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0) - return -1; - - ds.always_call_fn = 1; - ds.fn = collect_ambiguous; - ds.cb_data = &collect; - find_short_object_filename(&ds); - find_short_packed_object(&ds); - - ret = oid_array_for_each_unique(&collect, fn, cb_data); - oid_array_clear(&collect); - return ret; -} - -/* - * Return the slot of the most-significant bit set in "val". There are various - * ways to do this quickly with fls() or __builtin_clzl(), but speed is - * probably not a big deal here. - */ -static unsigned msb(unsigned long val) -{ - unsigned r = 0; - while (val >>= 1) - r++; - return r; -} - -struct min_abbrev_data { - unsigned int init_len; - unsigned int cur_len; - char *hex; - const struct object_id *oid; -}; - -static inline char get_hex_char_from_oid(const struct object_id *oid, - unsigned int pos) -{ - static const char hex[] = "0123456789abcdef"; - - if ((pos & 1) == 0) - return hex[oid->hash[pos >> 1] >> 4]; - else - return hex[oid->hash[pos >> 1] & 0xf]; -} - -static int extend_abbrev_len(const struct object_id *oid, void *cb_data) -{ - struct min_abbrev_data *mad = cb_data; - - unsigned int i = mad->init_len; - while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i)) - i++; - - if (i < GIT_MAX_RAWSZ && i >= mad->cur_len) - mad->cur_len = i + 1; - - return 0; -} - -static void find_abbrev_len_for_pack(struct packed_git *p, - struct min_abbrev_data *mad) -{ - int match = 0; - uint32_t num, first = 0; - struct object_id oid; - const struct object_id *mad_oid; - - if (open_pack_index(p) || !p->num_objects) - return; - - num = p->num_objects; - mad_oid = mad->oid; - match = bsearch_pack(mad_oid, p, &first); - - /* - * first is now the position in the packfile where we would insert - * mad->hash if it does not exist (or the position of mad->hash if - * it does exist). Hence, we consider a maximum of two objects - * nearby for the abbreviation length. - */ - mad->init_len = 0; - if (!match) { - if (nth_packed_object_oid(&oid, p, first)) - extend_abbrev_len(&oid, mad); - } else if (first < num - 1) { - if (nth_packed_object_oid(&oid, p, first + 1)) - extend_abbrev_len(&oid, mad); - } - if (first > 0) { - if (nth_packed_object_oid(&oid, p, first - 1)) - extend_abbrev_len(&oid, mad); - } - mad->init_len = mad->cur_len; -} - -static void find_abbrev_len_packed(struct min_abbrev_data *mad) -{ - struct packed_git *p; - - for (p = get_packed_git(the_repository); p; p = p->next) - find_abbrev_len_for_pack(p, mad); -} - -int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len) -{ - struct disambiguate_state ds; - struct min_abbrev_data mad; - struct object_id oid_ret; - if (len < 0) { - unsigned long count = approximate_object_count(); - /* - * Add one because the MSB only tells us the highest bit set, - * not including the value of all the _other_ bits (so "15" - * is only one off of 2^4, but the MSB is the 3rd bit. - */ - len = msb(count) + 1; - /* - * We now know we have on the order of 2^len objects, which - * expects a collision at 2^(len/2). But we also care about hex - * chars, not bits, and there are 4 bits per hex. So all - * together we need to divide by 2 and round up. - */ - len = DIV_ROUND_UP(len, 2); - /* - * For very small repos, we stick with our regular fallback. - */ - if (len < FALLBACK_DEFAULT_ABBREV) - len = FALLBACK_DEFAULT_ABBREV; - } - - oid_to_hex_r(hex, oid); - if (len == GIT_SHA1_HEXSZ || !len) - return GIT_SHA1_HEXSZ; - - mad.init_len = len; - mad.cur_len = len; - mad.hex = hex; - mad.oid = oid; - - find_abbrev_len_packed(&mad); - - if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0) - return -1; - - ds.fn = extend_abbrev_len; - ds.always_call_fn = 1; - ds.cb_data = (void *)&mad; - - find_short_object_filename(&ds); - (void)finish_object_disambiguation(&ds, &oid_ret); - - hex[mad.cur_len] = 0; - return mad.cur_len; -} - -const char *find_unique_abbrev(const struct object_id *oid, int len) -{ - static int bufno; - static char hexbuffer[4][GIT_MAX_HEXSZ + 1]; - char *hex = hexbuffer[bufno]; - bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); - find_unique_abbrev_r(hex, oid, len); - return hex; -} - -static int ambiguous_path(const char *path, int len) -{ - int slash = 1; - int cnt; - - for (cnt = 0; cnt < len; cnt++) { - switch (*path++) { - case '\0': - break; - case '/': - if (slash) - break; - slash = 1; - continue; - case '.': - continue; - default: - slash = 0; - continue; - } - break; - } - return slash; -} - -static inline int at_mark(const char *string, int len, - const char **suffix, int nr) -{ - int i; - - for (i = 0; i < nr; i++) { - int suffix_len = strlen(suffix[i]); - if (suffix_len <= len - && !strncasecmp(string, suffix[i], suffix_len)) - return suffix_len; - } - return 0; -} - -static inline int upstream_mark(const char *string, int len) -{ - const char *suffix[] = { "@{upstream}", "@{u}" }; - return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); -} - -static inline int push_mark(const char *string, int len) -{ - const char *suffix[] = { "@{push}" }; - return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); -} - -static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags); -static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf); - -static int get_oid_basic(const char *str, int len, struct object_id *oid, - unsigned int flags) -{ - static const char *warn_msg = "refname '%.*s' is ambiguous."; - static const char *object_name_msg = N_( - "Git normally never creates a ref that ends with 40 hex characters\n" - "because it will be ignored when you just specify 40-hex. These refs\n" - "may be created by mistake. For example,\n" - "\n" - " git checkout -b $br $(git rev-parse ...)\n" - "\n" - "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n" - "examine these refs and maybe delete them. Turn this message off by\n" - "running \"git config advice.objectNameWarning false\""); - struct object_id tmp_oid; - char *real_ref = NULL; - int refs_found = 0; - int at, reflog_len, nth_prior = 0; - - if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) { - if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) { - refs_found = dwim_ref(str, len, &tmp_oid, &real_ref); - if (refs_found > 0) { - warning(warn_msg, len, str); - if (advice_object_name_warning) - fprintf(stderr, "%s\n", _(object_name_msg)); - } - free(real_ref); - } - return 0; - } - - /* basic@{time or number or -number} format to query ref-log */ - reflog_len = at = 0; - if (len && str[len-1] == '}') { - for (at = len-4; at >= 0; at--) { - if (str[at] == '@' && str[at+1] == '{') { - if (str[at+2] == '-') { - if (at != 0) - /* @{-N} not at start */ - return -1; - nth_prior = 1; - continue; - } - if (!upstream_mark(str + at, len - at) && - !push_mark(str + at, len - at)) { - reflog_len = (len-1) - (at+2); - len = at; - } - break; - } - } - } - - /* Accept only unambiguous ref paths. */ - if (len && ambiguous_path(str, len)) - return -1; - - if (nth_prior) { - struct strbuf buf = STRBUF_INIT; - int detached; - - if (interpret_nth_prior_checkout(str, len, &buf) > 0) { - detached = (buf.len == GIT_SHA1_HEXSZ && !get_oid_hex(buf.buf, oid)); - strbuf_release(&buf); - if (detached) - return 0; - } - } - - if (!len && reflog_len) - /* allow "@{...}" to mean the current branch reflog */ - refs_found = dwim_ref("HEAD", 4, oid, &real_ref); - else if (reflog_len) - refs_found = dwim_log(str, len, oid, &real_ref); - else - refs_found = dwim_ref(str, len, oid, &real_ref); - - if (!refs_found) - return -1; - - if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) && - (refs_found > 1 || - !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY))) - warning(warn_msg, len, str); - - if (reflog_len) { - int nth, i; - timestamp_t at_time; - timestamp_t co_time; - int co_tz, co_cnt; - - /* Is it asking for N-th entry, or approxidate? */ - for (i = nth = 0; 0 <= nth && i < reflog_len; i++) { - char ch = str[at+2+i]; - if ('0' <= ch && ch <= '9') - nth = nth * 10 + ch - '0'; - else - nth = -1; - } - if (100000000 <= nth) { - at_time = nth; - nth = -1; - } else if (0 <= nth) - at_time = 0; - else { - int errors = 0; - char *tmp = xstrndup(str + at + 2, reflog_len); - at_time = approxidate_careful(tmp, &errors); - free(tmp); - if (errors) { - free(real_ref); - return -1; - } - } - if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL, - &co_time, &co_tz, &co_cnt)) { - if (!len) { - if (starts_with(real_ref, "refs/heads/")) { - str = real_ref + 11; - len = strlen(real_ref + 11); - } else { - /* detached HEAD */ - str = "HEAD"; - len = 4; - } - } - if (at_time) { - if (!(flags & GET_OID_QUIETLY)) { - warning("Log for '%.*s' only goes " - "back to %s.", len, str, - show_date(co_time, co_tz, DATE_MODE(RFC2822))); - } - } else { - if (flags & GET_OID_QUIETLY) { - exit(128); - } - die("Log for '%.*s' only has %d entries.", - len, str, co_cnt); - } - } - } - - free(real_ref); - return 0; -} - -static int get_parent(const char *name, int len, - struct object_id *result, int idx) -{ - struct object_id oid; - int ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH); - struct commit *commit; - struct commit_list *p; - - if (ret) - return ret; - commit = lookup_commit_reference(&oid); - if (parse_commit(commit)) - return -1; - if (!idx) { - oidcpy(result, &commit->object.oid); - return 0; - } - p = commit->parents; - while (p) { - if (!--idx) { - oidcpy(result, &p->item->object.oid); - return 0; - } - p = p->next; - } - return -1; -} - -static int get_nth_ancestor(const char *name, int len, - struct object_id *result, int generation) -{ - struct object_id oid; - struct commit *commit; - int ret; - - ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH); - if (ret) - return ret; - commit = lookup_commit_reference(&oid); - if (!commit) - return -1; - - while (generation--) { - if (parse_commit(commit) || !commit->parents) - return -1; - commit = commit->parents->item; - } - oidcpy(result, &commit->object.oid); - return 0; -} - -struct object *peel_to_type(const char *name, int namelen, - struct object *o, enum object_type expected_type) -{ - if (name && !namelen) - namelen = strlen(name); - while (1) { - if (!o || (!o->parsed && !parse_object(&o->oid))) - return NULL; - if (expected_type == OBJ_ANY || o->type == expected_type) - return o; - if (o->type == OBJ_TAG) - o = ((struct tag*) o)->tagged; - else if (o->type == OBJ_COMMIT) - o = &(((struct commit *) o)->tree->object); - else { - if (name) - error("%.*s: expected %s type, but the object " - "dereferences to %s type", - namelen, name, type_name(expected_type), - type_name(o->type)); - return NULL; - } - } -} - -static int peel_onion(const char *name, int len, struct object_id *oid, - unsigned lookup_flags) -{ - struct object_id outer; - const char *sp; - unsigned int expected_type = 0; - struct object *o; - - /* - * "ref^{type}" dereferences ref repeatedly until you cannot - * dereference anymore, or you get an object of given type, - * whichever comes first. "ref^{}" means just dereference - * tags until you get a non-tag. "ref^0" is a shorthand for - * "ref^{commit}". "commit^{tree}" could be used to find the - * top-level tree of the given commit. - */ - if (len < 4 || name[len-1] != '}') - return -1; - - for (sp = name + len - 1; name <= sp; sp--) { - int ch = *sp; - if (ch == '{' && name < sp && sp[-1] == '^') - break; - } - if (sp <= name) - return -1; - - sp++; /* beginning of type name, or closing brace for empty */ - if (starts_with(sp, "commit}")) - expected_type = OBJ_COMMIT; - else if (starts_with(sp, "tag}")) - expected_type = OBJ_TAG; - else if (starts_with(sp, "tree}")) - expected_type = OBJ_TREE; - else if (starts_with(sp, "blob}")) - expected_type = OBJ_BLOB; - else if (starts_with(sp, "object}")) - expected_type = OBJ_ANY; - else if (sp[0] == '}') - expected_type = OBJ_NONE; - else if (sp[0] == '/') - expected_type = OBJ_COMMIT; - else - return -1; - - lookup_flags &= ~GET_OID_DISAMBIGUATORS; - if (expected_type == OBJ_COMMIT) - lookup_flags |= GET_OID_COMMITTISH; - else if (expected_type == OBJ_TREE) - lookup_flags |= GET_OID_TREEISH; - - if (get_oid_1(name, sp - name - 2, &outer, lookup_flags)) - return -1; - - o = parse_object(&outer); - if (!o) - return -1; - if (!expected_type) { - o = deref_tag(o, name, sp - name - 2); - if (!o || (!o->parsed && !parse_object(&o->oid))) - return -1; - oidcpy(oid, &o->oid); - return 0; - } - - /* - * At this point, the syntax look correct, so - * if we do not get the needed object, we should - * barf. - */ - o = peel_to_type(name, len, o, expected_type); - if (!o) - return -1; - - oidcpy(oid, &o->oid); - if (sp[0] == '/') { - /* "$commit^{/foo}" */ - char *prefix; - int ret; - struct commit_list *list = NULL; - - /* - * $commit^{/}. Some regex implementation may reject. - * We don't need regex anyway. '' pattern always matches. - */ - if (sp[1] == '}') - return 0; - - prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); - commit_list_insert((struct commit *)o, &list); - ret = get_oid_oneline(prefix, oid, list); - free(prefix); - return ret; - } - return 0; -} - -static int get_describe_name(const char *name, int len, struct object_id *oid) -{ - const char *cp; - unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT; - - for (cp = name + len - 1; name + 2 <= cp; cp--) { - char ch = *cp; - if (!isxdigit(ch)) { - /* We must be looking at g in "SOMETHING-g" - * for it to be describe output. - */ - if (ch == 'g' && cp[-1] == '-') { - cp++; - len -= cp - name; - return get_short_oid(cp, len, oid, flags); - } - } - } - return -1; -} - -static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags) -{ - int ret, has_suffix; - const char *cp; - - /* - * "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1". - */ - has_suffix = 0; - for (cp = name + len - 1; name <= cp; cp--) { - int ch = *cp; - if ('0' <= ch && ch <= '9') - continue; - if (ch == '~' || ch == '^') - has_suffix = ch; - break; - } - - if (has_suffix) { - int num = 0; - int len1 = cp - name; - cp++; - while (cp < name + len) - num = num * 10 + *cp++ - '0'; - if (!num && len1 == len - 1) - num = 1; - if (has_suffix == '^') - return get_parent(name, len1, oid, num); - /* else if (has_suffix == '~') -- goes without saying */ - return get_nth_ancestor(name, len1, oid, num); - } - - ret = peel_onion(name, len, oid, lookup_flags); - if (!ret) - return 0; - - ret = get_oid_basic(name, len, oid, lookup_flags); - if (!ret) - return 0; - - /* It could be describe output that is "SOMETHING-gXXXX" */ - ret = get_describe_name(name, len, oid); - if (!ret) - return 0; - - return get_short_oid(name, len, oid, lookup_flags); -} - -/* - * This interprets names like ':/Initial revision of "git"' by searching - * through history and returning the first commit whose message starts - * the given regular expression. - * - * For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'. - * - * For a literal '!' character at the beginning of a pattern, you have to repeat - * that, like: ':/!!foo' - * - * For future extension, all other sequences beginning with ':/!' are reserved. - */ - -/* Remember to update object flag allocation in object.h */ -#define ONELINE_SEEN (1u<<20) - -static int handle_one_ref(const char *path, const struct object_id *oid, - int flag, void *cb_data) -{ - struct commit_list **list = cb_data; - struct object *object = parse_object(oid); - if (!object) - return 0; - if (object->type == OBJ_TAG) { - object = deref_tag(object, path, strlen(path)); - if (!object) - return 0; - } - if (object->type != OBJ_COMMIT) - return 0; - commit_list_insert((struct commit *)object, list); - return 0; -} - -static int get_oid_oneline(const char *prefix, struct object_id *oid, - struct commit_list *list) -{ - struct commit_list *backup = NULL, *l; - int found = 0; - int negative = 0; - regex_t regex; - - if (prefix[0] == '!') { - prefix++; - - if (prefix[0] == '-') { - prefix++; - negative = 1; - } else if (prefix[0] != '!') { - return -1; - } - } - - if (regcomp(®ex, prefix, REG_EXTENDED)) - return -1; - - for (l = list; l; l = l->next) { - l->item->object.flags |= ONELINE_SEEN; - commit_list_insert(l->item, &backup); - } - while (list) { - const char *p, *buf; - struct commit *commit; - int matches; - - commit = pop_most_recent_commit(&list, ONELINE_SEEN); - if (!parse_object(&commit->object.oid)) - continue; - buf = get_commit_buffer(commit, NULL); - p = strstr(buf, "\n\n"); - matches = negative ^ (p && !regexec(®ex, p + 2, 0, NULL, 0)); - unuse_commit_buffer(commit, buf); - - if (matches) { - oidcpy(oid, &commit->object.oid); - found = 1; - break; - } - } - regfree(®ex); - free_commit_list(list); - for (l = backup; l; l = l->next) - clear_commit_marks(l->item, ONELINE_SEEN); - free_commit_list(backup); - return found ? 0 : -1; -} - -struct grab_nth_branch_switch_cbdata { - int remaining; - struct strbuf buf; -}; - -static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) -{ - struct grab_nth_branch_switch_cbdata *cb = cb_data; - const char *match = NULL, *target = NULL; - size_t len; - - if (skip_prefix(message, "checkout: moving from ", &match)) - target = strstr(match, " to "); - - if (!match || !target) - return 0; - if (--(cb->remaining) == 0) { - len = target - match; - strbuf_reset(&cb->buf); - strbuf_add(&cb->buf, match, len); - return 1; /* we are done */ - } - return 0; -} - -/* - * Parse @{-N} syntax, return the number of characters parsed - * if successful; otherwise signal an error with negative value. - */ -static int interpret_nth_prior_checkout(const char *name, int namelen, - struct strbuf *buf) -{ - long nth; - int retval; - struct grab_nth_branch_switch_cbdata cb; - const char *brace; - char *num_end; - - if (namelen < 4) - return -1; - if (name[0] != '@' || name[1] != '{' || name[2] != '-') - return -1; - brace = memchr(name, '}', namelen); - if (!brace) - return -1; - nth = strtol(name + 3, &num_end, 10); - if (num_end != brace) - return -1; - if (nth <= 0) - return -1; - cb.remaining = nth; - strbuf_init(&cb.buf, 20); - - retval = 0; - if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) { - strbuf_reset(buf); - strbuf_addbuf(buf, &cb.buf); - retval = brace - name + 1; - } - - strbuf_release(&cb.buf); - return retval; -} - -int get_oid_mb(const char *name, struct object_id *oid) -{ - struct commit *one, *two; - struct commit_list *mbs; - struct object_id oid_tmp; - const char *dots; - int st; - - dots = strstr(name, "..."); - if (!dots) - return get_oid(name, oid); - if (dots == name) - st = get_oid("HEAD", &oid_tmp); - else { - struct strbuf sb; - strbuf_init(&sb, dots - name); - strbuf_add(&sb, name, dots - name); - st = get_oid_committish(sb.buf, &oid_tmp); - strbuf_release(&sb); - } - if (st) - return st; - one = lookup_commit_reference_gently(&oid_tmp, 0); - if (!one) - return -1; - - if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp)) - return -1; - two = lookup_commit_reference_gently(&oid_tmp, 0); - if (!two) - return -1; - mbs = get_merge_bases(one, two); - if (!mbs || mbs->next) - st = -1; - else { - st = 0; - oidcpy(oid, &mbs->item->object.oid); - } - free_commit_list(mbs); - return st; -} - -/* parse @something syntax, when 'something' is not {.*} */ -static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf) -{ - const char *next; - - if (len || name[1] == '{') - return -1; - - /* make sure it's a single @, or @@{.*}, not @foo */ - next = memchr(name + len + 1, '@', namelen - len - 1); - if (next && next[1] != '{') - return -1; - if (!next) - next = name + namelen; - if (next != name + 1) - return -1; - - strbuf_reset(buf); - strbuf_add(buf, "HEAD", 4); - return 1; -} - -static int reinterpret(const char *name, int namelen, int len, - struct strbuf *buf, unsigned allowed) -{ - /* we have extra data, which might need further processing */ - struct strbuf tmp = STRBUF_INIT; - int used = buf->len; - int ret; - - strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); - /* that data was not interpreted, remove our cruft */ - if (ret < 0) { - strbuf_setlen(buf, used); - return len; - } - strbuf_reset(buf); - strbuf_addbuf(buf, &tmp); - strbuf_release(&tmp); - /* tweak for size of {-N} versus expanded ref name */ - return ret - used + len; -} - -static void set_shortened_ref(struct strbuf *buf, const char *ref) -{ - char *s = shorten_unambiguous_ref(ref, 0); - strbuf_reset(buf); - strbuf_addstr(buf, s); - free(s); -} - -static int branch_interpret_allowed(const char *refname, unsigned allowed) -{ - if (!allowed) - return 1; - - if ((allowed & INTERPRET_BRANCH_LOCAL) && - starts_with(refname, "refs/heads/")) - return 1; - if ((allowed & INTERPRET_BRANCH_REMOTE) && - starts_with(refname, "refs/remotes/")) - return 1; - - return 0; -} - -static int interpret_branch_mark(const char *name, int namelen, - int at, struct strbuf *buf, - int (*get_mark)(const char *, int), - const char *(*get_data)(struct branch *, - struct strbuf *), - unsigned allowed) -{ - int len; - struct branch *branch; - struct strbuf err = STRBUF_INIT; - const char *value; - - len = get_mark(name + at, namelen - at); - if (!len) - return -1; - - if (memchr(name, ':', at)) - return -1; - - if (at) { - char *name_str = xmemdupz(name, at); - branch = branch_get(name_str); - free(name_str); - } else - branch = branch_get(NULL); - - value = get_data(branch, &err); - if (!value) - die("%s", err.buf); - - if (!branch_interpret_allowed(value, allowed)) - return -1; - - set_shortened_ref(buf, value); - return len + at; -} - -int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, - unsigned allowed) -{ - char *at; - const char *start; - int len; - - if (!namelen) - namelen = strlen(name); - - if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { - len = interpret_nth_prior_checkout(name, namelen, buf); - if (!len) { - return len; /* syntax Ok, not enough switches */ - } else if (len > 0) { - if (len == namelen) - return len; /* consumed all */ - else - return reinterpret(name, namelen, len, buf, allowed); - } - } - - for (start = name; - (at = memchr(start, '@', namelen - (start - name))); - start = at + 1) { - - if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { - len = interpret_empty_at(name, namelen, at - name, buf); - if (len > 0) - return reinterpret(name, namelen, len, buf, - allowed); - } - - len = interpret_branch_mark(name, namelen, at - name, buf, - upstream_mark, branch_get_upstream, - allowed); - if (len > 0) - return len; - - len = interpret_branch_mark(name, namelen, at - name, buf, - push_mark, branch_get_push, - allowed); - if (len > 0) - return len; - } - - return -1; -} - -void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) -{ - int len = strlen(name); - int used = interpret_branch_name(name, len, sb, allowed); - - if (used < 0) - used = 0; - strbuf_add(sb, name + used, len - used); -} - -int strbuf_check_branch_ref(struct strbuf *sb, const char *name) -{ - if (startup_info->have_repository) - strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); - else - strbuf_addstr(sb, name); - - /* - * This splice must be done even if we end up rejecting the - * name; builtin/branch.c::copy_or_rename_branch() still wants - * to see what the name expanded to so that "branch -m" can be - * used as a tool to correct earlier mistakes. - */ - strbuf_splice(sb, 0, 0, "refs/heads/", 11); - - if (*name == '-' || - !strcmp(sb->buf, "refs/heads/HEAD")) - return -1; - - return check_refname_format(sb->buf, 0); -} - -/* - * This is like "get_oid_basic()", except it allows "object ID expressions", - * notably "xyz^" for "parent of xyz" - */ -int get_oid(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, 0, oid, &unused); -} - - -/* - * Many callers know that the user meant to name a commit-ish by - * syntactical positions where the object name appears. Calling this - * function allows the machinery to disambiguate shorter-than-unique - * abbreviated object names between commit-ish and others. - * - * Note that this does NOT error out when the named object is not a - * commit-ish. It is merely to give a hint to the disambiguation - * machinery. - */ -int get_oid_committish(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, GET_OID_COMMITTISH, - oid, &unused); -} - -int get_oid_treeish(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, GET_OID_TREEISH, - oid, &unused); -} - -int get_oid_commit(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, GET_OID_COMMIT, - oid, &unused); -} - -int get_oid_tree(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, GET_OID_TREE, - oid, &unused); -} - -int get_oid_blob(const char *name, struct object_id *oid) -{ - struct object_context unused; - return get_oid_with_context(name, GET_OID_BLOB, - oid, &unused); -} - -/* Must be called only when object_name:filename doesn't exist. */ -static void diagnose_invalid_oid_path(const char *prefix, - const char *filename, - const struct object_id *tree_oid, - const char *object_name, - int object_name_len) -{ - struct object_id oid; - unsigned mode; - - if (!prefix) - prefix = ""; - - if (file_exists(filename)) - die("Path '%s' exists on disk, but not in '%.*s'.", - filename, object_name_len, object_name); - if (is_missing_file_error(errno)) { - char *fullname = xstrfmt("%s%s", prefix, filename); - - if (!get_tree_entry(tree_oid, fullname, &oid, &mode)) { - die("Path '%s' exists, but not '%s'.\n" - "Did you mean '%.*s:%s' aka '%.*s:./%s'?", - fullname, - filename, - object_name_len, object_name, - fullname, - object_name_len, object_name, - filename); - } - die("Path '%s' does not exist in '%.*s'", - filename, object_name_len, object_name); - } -} - -/* Must be called only when :stage:filename doesn't exist. */ -static void diagnose_invalid_index_path(int stage, - const char *prefix, - const char *filename) -{ - const struct cache_entry *ce; - int pos; - unsigned namelen = strlen(filename); - struct strbuf fullname = STRBUF_INIT; - - if (!prefix) - prefix = ""; - - /* Wrong stage number? */ - pos = cache_name_pos(filename, namelen); - if (pos < 0) - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, filename, namelen)) - die("Path '%s' is in the index, but not at stage %d.\n" - "Did you mean ':%d:%s'?", - filename, stage, - ce_stage(ce), filename); - } - - /* Confusion between relative and absolute filenames? */ - strbuf_addstr(&fullname, prefix); - strbuf_addstr(&fullname, filename); - pos = cache_name_pos(fullname.buf, fullname.len); - if (pos < 0) - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == fullname.len && - !memcmp(ce->name, fullname.buf, fullname.len)) - die("Path '%s' is in the index, but not '%s'.\n" - "Did you mean ':%d:%s' aka ':%d:./%s'?", - fullname.buf, filename, - ce_stage(ce), fullname.buf, - ce_stage(ce), filename); - } - - if (file_exists(filename)) - die("Path '%s' exists on disk, but not in the index.", filename); - if (is_missing_file_error(errno)) - die("Path '%s' does not exist (neither on disk nor in the index).", - filename); - - strbuf_release(&fullname); -} - - -static char *resolve_relative_path(const char *rel) -{ - if (!starts_with(rel, "./") && !starts_with(rel, "../")) - return NULL; - - if (!is_inside_work_tree()) - die("relative path syntax can't be used outside working tree."); - - /* die() inside prefix_path() if resolved path is outside worktree */ - return prefix_path(startup_info->prefix, - startup_info->prefix ? strlen(startup_info->prefix) : 0, - rel); -} - -static int get_oid_with_context_1(const char *name, - unsigned flags, - const char *prefix, - struct object_id *oid, - struct object_context *oc) -{ - int ret, bracket_depth; - int namelen = strlen(name); - const char *cp; - int only_to_die = flags & GET_OID_ONLY_TO_DIE; - - if (only_to_die) - flags |= GET_OID_QUIETLY; - - memset(oc, 0, sizeof(*oc)); - oc->mode = S_IFINVALID; - strbuf_init(&oc->symlink_path, 0); - ret = get_oid_1(name, namelen, oid, flags); - if (!ret) - return ret; - /* - * sha1:path --> object name of path in ent sha1 - * :path -> object name of absolute path in index - * :./path -> object name of path relative to cwd in index - * :[0-3]:path -> object name of path in index at stage - * :/foo -> recent commit matching foo - */ - if (name[0] == ':') { - int stage = 0; - const struct cache_entry *ce; - char *new_path = NULL; - int pos; - if (!only_to_die && namelen > 2 && name[1] == '/') { - struct commit_list *list = NULL; - - for_each_ref(handle_one_ref, &list); - commit_list_sort_by_date(&list); - return get_oid_oneline(name + 2, oid, list); - } - if (namelen < 3 || - name[2] != ':' || - name[1] < '0' || '3' < name[1]) - cp = name + 1; - else { - stage = name[1] - '0'; - cp = name + 3; - } - new_path = resolve_relative_path(cp); - if (!new_path) { - namelen = namelen - (cp - name); - } else { - cp = new_path; - namelen = strlen(cp); - } - - if (flags & GET_OID_RECORD_PATH) - oc->path = xstrdup(cp); - - if (!active_cache) - read_cache(); - pos = cache_name_pos(cp, namelen); - if (pos < 0) - pos = -pos - 1; - while (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) != namelen || - memcmp(ce->name, cp, namelen)) - break; - if (ce_stage(ce) == stage) { - oidcpy(oid, &ce->oid); - oc->mode = ce->ce_mode; - free(new_path); - return 0; - } - pos++; - } - if (only_to_die && name[1] && name[1] != '/') - diagnose_invalid_index_path(stage, prefix, cp); - free(new_path); - return -1; - } - for (cp = name, bracket_depth = 0; *cp; cp++) { - if (*cp == '{') - bracket_depth++; - else if (bracket_depth && *cp == '}') - bracket_depth--; - else if (!bracket_depth && *cp == ':') - break; - } - if (*cp == ':') { - struct object_id tree_oid; - int len = cp - name; - unsigned sub_flags = flags; - - sub_flags &= ~GET_OID_DISAMBIGUATORS; - sub_flags |= GET_OID_TREEISH; - - if (!get_oid_1(name, len, &tree_oid, sub_flags)) { - const char *filename = cp+1; - char *new_filename = NULL; - - new_filename = resolve_relative_path(filename); - if (new_filename) - filename = new_filename; - if (flags & GET_OID_FOLLOW_SYMLINKS) { - ret = get_tree_entry_follow_symlinks(tree_oid.hash, - filename, oid->hash, &oc->symlink_path, - &oc->mode); - } else { - ret = get_tree_entry(&tree_oid, filename, oid, - &oc->mode); - if (ret && only_to_die) { - diagnose_invalid_oid_path(prefix, - filename, - &tree_oid, - name, len); - } - } - hashcpy(oc->tree, tree_oid.hash); - if (flags & GET_OID_RECORD_PATH) - oc->path = xstrdup(filename); - - free(new_filename); - return ret; - } else { - if (only_to_die) - die("Invalid object name '%.*s'.", len, name); - } - } - return ret; -} - -/* - * Call this function when you know "name" given by the end user must - * name an object but it doesn't; the function _may_ die with a better - * diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not - * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case - * you have a chance to diagnose the error further. - */ -void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) -{ - struct object_context oc; - struct object_id oid; - get_oid_with_context_1(name, GET_OID_ONLY_TO_DIE, prefix, &oid, &oc); -} - -int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc) -{ - if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE) - die("BUG: incompatible flags for get_sha1_with_context"); - return get_oid_with_context_1(str, flags, NULL, oid, oc); -} -- cgit v1.2.1 From fc1395f4a491a7da46a446233531005634eb979d Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:20 -0700 Subject: sha1_file.c: rename to use dash in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Signed-off-by: Stefan Beller --- Documentation/technical/api-object-access.txt | 2 +- Makefile | 2 +- builtin/index-pack.c | 2 +- sha1-file.c | 2254 +++++++++++++++++++++++++ sha1_file.c | 2254 ------------------------- 5 files changed, 2257 insertions(+), 2257 deletions(-) create mode 100644 sha1-file.c delete mode 100644 sha1_file.c diff --git a/Documentation/technical/api-object-access.txt b/Documentation/technical/api-object-access.txt index a1162e5bcd..5b29622d00 100644 --- a/Documentation/technical/api-object-access.txt +++ b/Documentation/technical/api-object-access.txt @@ -1,7 +1,7 @@ object access API ================= -Talk about and family, things like +Talk about and family, things like * read_sha1_file() * read_object_with_reference() diff --git a/Makefile b/Makefile index ed0ac30a16..ce26efa7dc 100644 --- a/Makefile +++ b/Makefile @@ -900,7 +900,7 @@ LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o LIB_OBJS += sha1-lookup.o -LIB_OBJS += sha1_file.o +LIB_OBJS += sha1-file.o LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 7700907f1b..78e66b9986 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1593,7 +1593,7 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name) /* * Get rid of the idx file as we do not need it anymore. * NEEDSWORK: extract this bit from free_pack_by_name() in - * sha1_file.c, perhaps? It shouldn't matter very much as we + * sha1-file.c, perhaps? It shouldn't matter very much as we * know we haven't installed this pack (hence we never have * read anything from it). */ diff --git a/sha1-file.c b/sha1-file.c new file mode 100644 index 0000000000..77ccaab928 --- /dev/null +++ b/sha1-file.c @@ -0,0 +1,2254 @@ +/* + * GIT - The information manager from hell + * + * Copyright (C) Linus Torvalds, 2005 + * + * This handles basic git sha1 object files - packing, unpacking, + * creation etc. + */ +#include "cache.h" +#include "config.h" +#include "string-list.h" +#include "lockfile.h" +#include "delta.h" +#include "pack.h" +#include "blob.h" +#include "commit.h" +#include "run-command.h" +#include "tag.h" +#include "tree.h" +#include "tree-walk.h" +#include "refs.h" +#include "pack-revindex.h" +#include "sha1-lookup.h" +#include "bulk-checkin.h" +#include "repository.h" +#include "streaming.h" +#include "dir.h" +#include "list.h" +#include "mergesort.h" +#include "quote.h" +#include "packfile.h" +#include "fetch-object.h" +#include "object-store.h" + +/* The maximum size for an object header. */ +#define MAX_HEADER_LEN 32 + +const unsigned char null_sha1[GIT_MAX_RAWSZ]; +const struct object_id null_oid; +const struct object_id empty_tree_oid = { + EMPTY_TREE_SHA1_BIN_LITERAL +}; +const struct object_id empty_blob_oid = { + EMPTY_BLOB_SHA1_BIN_LITERAL +}; + +static void git_hash_sha1_init(git_hash_ctx *ctx) +{ + git_SHA1_Init(&ctx->sha1); +} + +static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + git_SHA1_Update(&ctx->sha1, data, len); +} + +static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx) +{ + git_SHA1_Final(hash, &ctx->sha1); +} + +static void git_hash_unknown_init(git_hash_ctx *ctx) +{ + die("trying to init unknown hash"); +} + +static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + die("trying to update unknown hash"); +} + +static void git_hash_unknown_final(unsigned char *hash, git_hash_ctx *ctx) +{ + die("trying to finalize unknown hash"); +} + +const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { + { + NULL, + 0x00000000, + 0, + 0, + git_hash_unknown_init, + git_hash_unknown_update, + git_hash_unknown_final, + NULL, + NULL, + }, + { + "sha-1", + /* "sha1", big-endian */ + 0x73686131, + GIT_SHA1_RAWSZ, + GIT_SHA1_HEXSZ, + git_hash_sha1_init, + git_hash_sha1_update, + git_hash_sha1_final, + &empty_tree_oid, + &empty_blob_oid, + }, +}; + +/* + * This is meant to hold a *small* number of objects that you would + * want read_sha1_file() to be able to return, but yet you do not want + * to write them into the object store (e.g. a browse-only + * application). + */ +static struct cached_object { + unsigned char sha1[20]; + enum object_type type; + void *buf; + unsigned long size; +} *cached_objects; +static int cached_object_nr, cached_object_alloc; + +static struct cached_object empty_tree = { + EMPTY_TREE_SHA1_BIN_LITERAL, + OBJ_TREE, + "", + 0 +}; + +static struct cached_object *find_cached_object(const unsigned char *sha1) +{ + int i; + struct cached_object *co = cached_objects; + + for (i = 0; i < cached_object_nr; i++, co++) { + if (!hashcmp(co->sha1, sha1)) + return co; + } + if (!hashcmp(sha1, empty_tree.sha1)) + return &empty_tree; + return NULL; +} + + +static int get_conv_flags(unsigned flags) +{ + if (flags & HASH_RENORMALIZE) + return CONV_EOL_RENORMALIZE; + else if (flags & HASH_WRITE_OBJECT) + return global_conv_flags_eol; + else + return 0; +} + + +int mkdir_in_gitdir(const char *path) +{ + if (mkdir(path, 0777)) { + int saved_errno = errno; + struct stat st; + struct strbuf sb = STRBUF_INIT; + + if (errno != EEXIST) + return -1; + /* + * Are we looking at a path in a symlinked worktree + * whose original repository does not yet have it? + * e.g. .git/rr-cache pointing at its original + * repository in which the user hasn't performed any + * conflict resolution yet? + */ + if (lstat(path, &st) || !S_ISLNK(st.st_mode) || + strbuf_readlink(&sb, path, st.st_size) || + !is_absolute_path(sb.buf) || + mkdir(sb.buf, 0777)) { + strbuf_release(&sb); + errno = saved_errno; + return -1; + } + strbuf_release(&sb); + } + return adjust_shared_perm(path); +} + +enum scld_error safe_create_leading_directories(char *path) +{ + char *next_component = path + offset_1st_component(path); + enum scld_error ret = SCLD_OK; + + while (ret == SCLD_OK && next_component) { + struct stat st; + char *slash = next_component, slash_character; + + while (*slash && !is_dir_sep(*slash)) + slash++; + + if (!*slash) + break; + + next_component = slash + 1; + while (is_dir_sep(*next_component)) + next_component++; + if (!*next_component) + break; + + slash_character = *slash; + *slash = '\0'; + if (!stat(path, &st)) { + /* path exists */ + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + ret = SCLD_EXISTS; + } + } else if (mkdir(path, 0777)) { + if (errno == EEXIST && + !stat(path, &st) && S_ISDIR(st.st_mode)) + ; /* somebody created it since we checked */ + else if (errno == ENOENT) + /* + * Either mkdir() failed because + * somebody just pruned the containing + * directory, or stat() failed because + * the file that was in our way was + * just removed. Either way, inform + * the caller that it might be worth + * trying again: + */ + ret = SCLD_VANISHED; + else + ret = SCLD_FAILED; + } else if (adjust_shared_perm(path)) { + ret = SCLD_PERMS; + } + *slash = slash_character; + } + return ret; +} + +enum scld_error safe_create_leading_directories_const(const char *path) +{ + int save_errno; + /* path points to cache entries, so xstrdup before messing with it */ + char *buf = xstrdup(path); + enum scld_error result = safe_create_leading_directories(buf); + + save_errno = errno; + free(buf); + errno = save_errno; + return result; +} + +int raceproof_create_file(const char *path, create_file_fn fn, void *cb) +{ + /* + * The number of times we will try to remove empty directories + * in the way of path. This is only 1 because if another + * process is racily creating directories that conflict with + * us, we don't want to fight against them. + */ + int remove_directories_remaining = 1; + + /* + * The number of times that we will try to create the + * directories containing path. We are willing to attempt this + * more than once, because another process could be trying to + * clean up empty directories at the same time as we are + * trying to create them. + */ + int create_directories_remaining = 3; + + /* A scratch copy of path, filled lazily if we need it: */ + struct strbuf path_copy = STRBUF_INIT; + + int ret, save_errno; + + /* Sanity check: */ + assert(*path); + +retry_fn: + ret = fn(path, cb); + save_errno = errno; + if (!ret) + goto out; + + if (errno == EISDIR && remove_directories_remaining-- > 0) { + /* + * A directory is in the way. Maybe it is empty; try + * to remove it: + */ + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY)) + goto retry_fn; + } else if (errno == ENOENT && create_directories_remaining-- > 0) { + /* + * Maybe the containing directory didn't exist, or + * maybe it was just deleted by a process that is + * racing with us to clean up empty directories. Try + * to create it: + */ + enum scld_error scld_result; + + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + do { + scld_result = safe_create_leading_directories(path_copy.buf); + if (scld_result == SCLD_OK) + goto retry_fn; + } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0); + } + +out: + strbuf_release(&path_copy); + errno = save_errno; + return ret; +} + +static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1) +{ + int i; + for (i = 0; i < 20; i++) { + static char hex[] = "0123456789abcdef"; + unsigned int val = sha1[i]; + strbuf_addch(buf, hex[val >> 4]); + strbuf_addch(buf, hex[val & 0xf]); + if (!i) + strbuf_addch(buf, '/'); + } +} + +void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned char *sha1) +{ + strbuf_addstr(buf, r->objects->objectdir); + strbuf_addch(buf, '/'); + fill_sha1_path(buf, sha1); +} + +struct strbuf *alt_scratch_buf(struct alternate_object_database *alt) +{ + strbuf_setlen(&alt->scratch, alt->base_len); + return &alt->scratch; +} + +static const char *alt_sha1_path(struct alternate_object_database *alt, + const unsigned char *sha1) +{ + struct strbuf *buf = alt_scratch_buf(alt); + fill_sha1_path(buf, sha1); + return buf->buf; +} + +/* + * Return non-zero iff the path is usable as an alternate object database. + */ +static int alt_odb_usable(struct raw_object_store *o, + struct strbuf *path, + const char *normalized_objdir) +{ + struct alternate_object_database *alt; + + /* Detect cases where alternate disappeared */ + if (!is_directory(path->buf)) { + error("object directory %s does not exist; " + "check .git/objects/info/alternates.", + path->buf); + return 0; + } + + /* + * Prevent the common mistake of listing the same + * thing twice, or object directory itself. + */ + for (alt = o->alt_odb_list; alt; alt = alt->next) { + if (!fspathcmp(path->buf, alt->path)) + return 0; + } + if (!fspathcmp(path->buf, normalized_objdir)) + return 0; + + return 1; +} + +/* + * Prepare alternate object database registry. + * + * The variable alt_odb_list points at the list of struct + * alternate_object_database. The elements on this list come from + * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT + * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, + * whose contents is similar to that environment variable but can be + * LF separated. Its base points at a statically allocated buffer that + * contains "/the/directory/corresponding/to/.git/objects/...", while + * its name points just after the slash at the end of ".git/objects/" + * in the example above, and has enough space to hold 40-byte hex + * SHA1, an extra slash for the first level indirection, and the + * terminating NUL. + */ +static void read_info_alternates(struct repository *r, + const char *relative_base, + int depth); +static int link_alt_odb_entry(struct repository *r, const char *entry, + const char *relative_base, int depth, const char *normalized_objdir) +{ + struct alternate_object_database *ent; + struct strbuf pathbuf = STRBUF_INIT; + + if (!is_absolute_path(entry) && relative_base) { + strbuf_realpath(&pathbuf, relative_base, 1); + strbuf_addch(&pathbuf, '/'); + } + strbuf_addstr(&pathbuf, entry); + + if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) { + error("unable to normalize alternate object path: %s", + pathbuf.buf); + strbuf_release(&pathbuf); + return -1; + } + + /* + * The trailing slash after the directory name is given by + * this function at the end. Remove duplicates. + */ + while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/') + strbuf_setlen(&pathbuf, pathbuf.len - 1); + + if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir)) { + strbuf_release(&pathbuf); + return -1; + } + + ent = alloc_alt_odb(pathbuf.buf); + + /* add the alternate entry */ + *r->objects->alt_odb_tail = ent; + r->objects->alt_odb_tail = &(ent->next); + ent->next = NULL; + + /* recursively add alternates */ + read_info_alternates(r, pathbuf.buf, depth + 1); + + strbuf_release(&pathbuf); + return 0; +} + +static const char *parse_alt_odb_entry(const char *string, + int sep, + struct strbuf *out) +{ + const char *end; + + strbuf_reset(out); + + if (*string == '#') { + /* comment; consume up to next separator */ + end = strchrnul(string, sep); + } else if (*string == '"' && !unquote_c_style(out, string, &end)) { + /* + * quoted path; unquote_c_style has copied the + * data for us and set "end". Broken quoting (e.g., + * an entry that doesn't end with a quote) falls + * back to the unquoted case below. + */ + } else { + /* normal, unquoted path */ + end = strchrnul(string, sep); + strbuf_add(out, string, end - string); + } + + if (*end) + end++; + return end; +} + +static void link_alt_odb_entries(struct repository *r, const char *alt, + int sep, const char *relative_base, int depth) +{ + struct strbuf objdirbuf = STRBUF_INIT; + struct strbuf entry = STRBUF_INIT; + + if (!alt || !*alt) + return; + + if (depth > 5) { + error("%s: ignoring alternate object stores, nesting too deep.", + relative_base); + return; + } + + strbuf_add_absolute_path(&objdirbuf, r->objects->objectdir); + if (strbuf_normalize_path(&objdirbuf) < 0) + die("unable to normalize object directory: %s", + objdirbuf.buf); + + while (*alt) { + alt = parse_alt_odb_entry(alt, sep, &entry); + if (!entry.len) + continue; + link_alt_odb_entry(r, entry.buf, + relative_base, depth, objdirbuf.buf); + } + strbuf_release(&entry); + strbuf_release(&objdirbuf); +} + +static void read_info_alternates(struct repository *r, + const char *relative_base, + int depth) +{ + char *path; + struct strbuf buf = STRBUF_INIT; + + path = xstrfmt("%s/info/alternates", relative_base); + if (strbuf_read_file(&buf, path, 1024) < 0) { + warn_on_fopen_errors(path); + free(path); + return; + } + + link_alt_odb_entries(r, buf.buf, '\n', relative_base, depth); + strbuf_release(&buf); + free(path); +} + +struct alternate_object_database *alloc_alt_odb(const char *dir) +{ + struct alternate_object_database *ent; + + FLEX_ALLOC_STR(ent, path, dir); + strbuf_init(&ent->scratch, 0); + strbuf_addf(&ent->scratch, "%s/", dir); + ent->base_len = ent->scratch.len; + + return ent; +} + +void add_to_alternates_file(const char *reference) +{ + struct lock_file lock = LOCK_INIT; + char *alts = git_pathdup("objects/info/alternates"); + FILE *in, *out; + int found = 0; + + hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR); + out = fdopen_lock_file(&lock, "w"); + if (!out) + die_errno("unable to fdopen alternates lockfile"); + + in = fopen(alts, "r"); + if (in) { + struct strbuf line = STRBUF_INIT; + + while (strbuf_getline(&line, in) != EOF) { + if (!strcmp(reference, line.buf)) { + found = 1; + break; + } + fprintf_or_die(out, "%s\n", line.buf); + } + + strbuf_release(&line); + fclose(in); + } + else if (errno != ENOENT) + die_errno("unable to read alternates file"); + + if (found) { + rollback_lock_file(&lock); + } else { + fprintf_or_die(out, "%s\n", reference); + if (commit_lock_file(&lock)) + die_errno("unable to move new alternates file into place"); + if (the_repository->objects->alt_odb_tail) + link_alt_odb_entries(the_repository, reference, + '\n', NULL, 0); + } + free(alts); +} + +void add_to_alternates_memory(const char *reference) +{ + /* + * Make sure alternates are initialized, or else our entry may be + * overwritten when they are. + */ + prepare_alt_odb(the_repository); + + link_alt_odb_entries(the_repository, reference, + '\n', NULL, 0); +} + +/* + * Compute the exact path an alternate is at and returns it. In case of + * error NULL is returned and the human readable error is added to `err` + * `path` may be relative and should point to $GITDIR. + * `err` must not be null. + */ +char *compute_alternate_path(const char *path, struct strbuf *err) +{ + char *ref_git = NULL; + const char *repo, *ref_git_s; + int seen_error = 0; + + ref_git_s = real_path_if_valid(path); + if (!ref_git_s) { + seen_error = 1; + strbuf_addf(err, _("path '%s' does not exist"), path); + goto out; + } else + /* + * Beware: read_gitfile(), real_path() and mkpath() + * return static buffer + */ + ref_git = xstrdup(ref_git_s); + + repo = read_gitfile(ref_git); + if (!repo) + repo = read_gitfile(mkpath("%s/.git", ref_git)); + if (repo) { + free(ref_git); + ref_git = xstrdup(repo); + } + + if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) { + char *ref_git_git = mkpathdup("%s/.git", ref_git); + free(ref_git); + ref_git = ref_git_git; + } else if (!is_directory(mkpath("%s/objects", ref_git))) { + struct strbuf sb = STRBUF_INIT; + seen_error = 1; + if (get_common_dir(&sb, ref_git)) { + strbuf_addf(err, + _("reference repository '%s' as a linked " + "checkout is not supported yet."), + path); + goto out; + } + + strbuf_addf(err, _("reference repository '%s' is not a " + "local repository."), path); + goto out; + } + + if (!access(mkpath("%s/shallow", ref_git), F_OK)) { + strbuf_addf(err, _("reference repository '%s' is shallow"), + path); + seen_error = 1; + goto out; + } + + if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) { + strbuf_addf(err, + _("reference repository '%s' is grafted"), + path); + seen_error = 1; + goto out; + } + +out: + if (seen_error) { + FREE_AND_NULL(ref_git); + } + + return ref_git; +} + +int foreach_alt_odb(alt_odb_fn fn, void *cb) +{ + struct alternate_object_database *ent; + int r = 0; + + prepare_alt_odb(the_repository); + for (ent = the_repository->objects->alt_odb_list; ent; ent = ent->next) { + r = fn(ent, cb); + if (r) + break; + } + return r; +} + +void prepare_alt_odb(struct repository *r) +{ + if (r->objects->alt_odb_tail) + return; + + r->objects->alt_odb_tail = &r->objects->alt_odb_list; + link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0); + + read_info_alternates(r, r->objects->objectdir, 0); +} + +/* Returns 1 if we have successfully freshened the file, 0 otherwise. */ +static int freshen_file(const char *fn) +{ + struct utimbuf t; + t.actime = t.modtime = time(NULL); + return !utime(fn, &t); +} + +/* + * All of the check_and_freshen functions return 1 if the file exists and was + * freshened (if freshening was requested), 0 otherwise. If they return + * 0, you should not assume that it is safe to skip a write of the object (it + * either does not exist on disk, or has a stale mtime and may be subject to + * pruning). + */ +int check_and_freshen_file(const char *fn, int freshen) +{ + if (access(fn, F_OK)) + return 0; + if (freshen && !freshen_file(fn)) + return 0; + return 1; +} + +static int check_and_freshen_local(const unsigned char *sha1, int freshen) +{ + static struct strbuf buf = STRBUF_INIT; + + strbuf_reset(&buf); + sha1_file_name(the_repository, &buf, sha1); + + return check_and_freshen_file(buf.buf, freshen); +} + +static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen) +{ + struct alternate_object_database *alt; + prepare_alt_odb(the_repository); + for (alt = the_repository->objects->alt_odb_list; alt; alt = alt->next) { + const char *path = alt_sha1_path(alt, sha1); + if (check_and_freshen_file(path, freshen)) + return 1; + } + return 0; +} + +static int check_and_freshen(const unsigned char *sha1, int freshen) +{ + return check_and_freshen_local(sha1, freshen) || + check_and_freshen_nonlocal(sha1, freshen); +} + +int has_loose_object_nonlocal(const unsigned char *sha1) +{ + return check_and_freshen_nonlocal(sha1, 0); +} + +static int has_loose_object(const unsigned char *sha1) +{ + return check_and_freshen(sha1, 0); +} + +static void mmap_limit_check(size_t length) +{ + static size_t limit = 0; + if (!limit) { + limit = git_env_ulong("GIT_MMAP_LIMIT", 0); + if (!limit) + limit = SIZE_MAX; + } + if (length > limit) + die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX, + (uintmax_t)length, (uintmax_t)limit); +} + +void *xmmap_gently(void *start, size_t length, + int prot, int flags, int fd, off_t offset) +{ + void *ret; + + mmap_limit_check(length); + ret = mmap(start, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) { + if (!length) + return NULL; + release_pack_memory(length); + ret = mmap(start, length, prot, flags, fd, offset); + } + return ret; +} + +void *xmmap(void *start, size_t length, + int prot, int flags, int fd, off_t offset) +{ + void *ret = xmmap_gently(start, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) + die_errno("mmap failed"); + return ret; +} + +/* + * With an in-core object data in "map", rehash it to make sure the + * object name actually matches "sha1" to detect object corruption. + * With "map" == NULL, try reading the object named with "sha1" using + * the streaming interface and rehash it to do the same. + */ +int check_object_signature(const struct object_id *oid, void *map, + unsigned long size, const char *type) +{ + struct object_id real_oid; + enum object_type obj_type; + struct git_istream *st; + git_hash_ctx c; + char hdr[MAX_HEADER_LEN]; + int hdrlen; + + if (map) { + hash_object_file(map, size, type, &real_oid); + return oidcmp(oid, &real_oid) ? -1 : 0; + } + + st = open_istream(oid, &obj_type, &size, NULL); + if (!st) + return -1; + + /* Generate the header */ + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(obj_type), size) + 1; + + /* Sha1.. */ + the_hash_algo->init_fn(&c); + the_hash_algo->update_fn(&c, hdr, hdrlen); + for (;;) { + char buf[1024 * 16]; + ssize_t readlen = read_istream(st, buf, sizeof(buf)); + + if (readlen < 0) { + close_istream(st); + return -1; + } + if (!readlen) + break; + the_hash_algo->update_fn(&c, buf, readlen); + } + the_hash_algo->final_fn(real_oid.hash, &c); + close_istream(st); + return oidcmp(oid, &real_oid) ? -1 : 0; +} + +int git_open_cloexec(const char *name, int flags) +{ + int fd; + static int o_cloexec = O_CLOEXEC; + + fd = open(name, flags | o_cloexec); + if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) { + /* Try again w/o O_CLOEXEC: the kernel might not support it */ + o_cloexec &= ~O_CLOEXEC; + fd = open(name, flags | o_cloexec); + } + +#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) + { + static int fd_cloexec = FD_CLOEXEC; + + if (!o_cloexec && 0 <= fd && fd_cloexec) { + /* Opened w/o O_CLOEXEC? try with fcntl(2) to add it */ + int flags = fcntl(fd, F_GETFD); + if (fcntl(fd, F_SETFD, flags | fd_cloexec)) + fd_cloexec = 0; + } + } +#endif + return fd; +} + +/* + * Find "sha1" as a loose object in the local repository or in an alternate. + * Returns 0 on success, negative on failure. + * + * The "path" out-parameter will give the path of the object we found (if any). + * Note that it may point to static storage and is only valid until another + * call to sha1_file_name(), etc. + */ +static int stat_sha1_file(struct repository *r, const unsigned char *sha1, + struct stat *st, const char **path) +{ + struct alternate_object_database *alt; + static struct strbuf buf = STRBUF_INIT; + + strbuf_reset(&buf); + sha1_file_name(r, &buf, sha1); + *path = buf.buf; + + if (!lstat(*path, st)) + return 0; + + prepare_alt_odb(r); + errno = ENOENT; + for (alt = r->objects->alt_odb_list; alt; alt = alt->next) { + *path = alt_sha1_path(alt, sha1); + if (!lstat(*path, st)) + return 0; + } + + return -1; +} + +/* + * Like stat_sha1_file(), but actually open the object and return the + * descriptor. See the caveats on the "path" parameter above. + */ +static int open_sha1_file(struct repository *r, + const unsigned char *sha1, const char **path) +{ + int fd; + struct alternate_object_database *alt; + int most_interesting_errno; + static struct strbuf buf = STRBUF_INIT; + + strbuf_reset(&buf); + sha1_file_name(r, &buf, sha1); + *path = buf.buf; + + fd = git_open(*path); + if (fd >= 0) + return fd; + most_interesting_errno = errno; + + prepare_alt_odb(r); + for (alt = r->objects->alt_odb_list; alt; alt = alt->next) { + *path = alt_sha1_path(alt, sha1); + fd = git_open(*path); + if (fd >= 0) + return fd; + if (most_interesting_errno == ENOENT) + most_interesting_errno = errno; + } + errno = most_interesting_errno; + return -1; +} + +/* + * Map the loose object at "path" if it is not NULL, or the path found by + * searching for a loose object named "sha1". + */ +static void *map_sha1_file_1(struct repository *r, const char *path, + const unsigned char *sha1, unsigned long *size) +{ + void *map; + int fd; + + if (path) + fd = git_open(path); + else + fd = open_sha1_file(r, sha1, &path); + map = NULL; + if (fd >= 0) { + struct stat st; + + if (!fstat(fd, &st)) { + *size = xsize_t(st.st_size); + if (!*size) { + /* mmap() is forbidden on empty files */ + error("object file %s is empty", path); + return NULL; + } + map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); + } + close(fd); + } + return map; +} + +void *map_sha1_file(struct repository *r, + const unsigned char *sha1, unsigned long *size) +{ + return map_sha1_file_1(r, NULL, sha1, size); +} + +static int unpack_sha1_short_header(git_zstream *stream, + unsigned char *map, unsigned long mapsize, + void *buffer, unsigned long bufsiz) +{ + /* Get the data stream */ + memset(stream, 0, sizeof(*stream)); + stream->next_in = map; + stream->avail_in = mapsize; + stream->next_out = buffer; + stream->avail_out = bufsiz; + + git_inflate_init(stream); + return git_inflate(stream, 0); +} + +int unpack_sha1_header(git_zstream *stream, + unsigned char *map, unsigned long mapsize, + void *buffer, unsigned long bufsiz) +{ + int status = unpack_sha1_short_header(stream, map, mapsize, + buffer, bufsiz); + + if (status < Z_OK) + return status; + + /* Make sure we have the terminating NUL */ + if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) + return -1; + return 0; +} + +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map, + unsigned long mapsize, void *buffer, + unsigned long bufsiz, struct strbuf *header) +{ + int status; + + status = unpack_sha1_short_header(stream, map, mapsize, buffer, bufsiz); + if (status < Z_OK) + return -1; + + /* + * Check if entire header is unpacked in the first iteration. + */ + if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) + return 0; + + /* + * buffer[0..bufsiz] was not large enough. Copy the partial + * result out to header, and then append the result of further + * reading the stream. + */ + strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); + stream->next_out = buffer; + stream->avail_out = bufsiz; + + do { + status = git_inflate(stream, 0); + strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); + if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) + return 0; + stream->next_out = buffer; + stream->avail_out = bufsiz; + } while (status != Z_STREAM_END); + return -1; +} + +static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1) +{ + int bytes = strlen(buffer) + 1; + unsigned char *buf = xmallocz(size); + unsigned long n; + int status = Z_OK; + + n = stream->total_out - bytes; + if (n > size) + n = size; + memcpy(buf, (char *) buffer + bytes, n); + bytes = n; + if (bytes <= size) { + /* + * The above condition must be (bytes <= size), not + * (bytes < size). In other words, even though we + * expect no more output and set avail_out to zero, + * the input zlib stream may have bytes that express + * "this concludes the stream", and we *do* want to + * eat that input. + * + * Otherwise we would not be able to test that we + * consumed all the input to reach the expected size; + * we also want to check that zlib tells us that all + * went well with status == Z_STREAM_END at the end. + */ + stream->next_out = buf + bytes; + stream->avail_out = size - bytes; + while (status == Z_OK) + status = git_inflate(stream, Z_FINISH); + } + if (status == Z_STREAM_END && !stream->avail_in) { + git_inflate_end(stream); + return buf; + } + + if (status < 0) + error("corrupt loose object '%s'", sha1_to_hex(sha1)); + else if (stream->avail_in) + error("garbage at end of loose object '%s'", + sha1_to_hex(sha1)); + free(buf); + return NULL; +} + +/* + * We used to just use "sscanf()", but that's actually way + * too permissive for what we want to check. So do an anal + * object header parse by hand. + */ +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi, + unsigned int flags) +{ + const char *type_buf = hdr; + unsigned long size; + int type, type_len = 0; + + /* + * The type can be of any size but is followed by + * a space. + */ + for (;;) { + char c = *hdr++; + if (!c) + return -1; + if (c == ' ') + break; + type_len++; + } + + type = type_from_string_gently(type_buf, type_len, 1); + if (oi->type_name) + strbuf_add(oi->type_name, type_buf, type_len); + /* + * Set type to 0 if its an unknown object and + * we're obtaining the type using '--allow-unknown-type' + * option. + */ + if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0)) + type = 0; + else if (type < 0) + die("invalid object type"); + if (oi->typep) + *oi->typep = type; + + /* + * The length must follow immediately, and be in canonical + * decimal format (ie "010" is not valid). + */ + size = *hdr++ - '0'; + if (size > 9) + return -1; + if (size) { + for (;;) { + unsigned long c = *hdr - '0'; + if (c > 9) + break; + hdr++; + size = size * 10 + c; + } + } + + if (oi->sizep) + *oi->sizep = size; + + /* + * The length must be followed by a zero byte + */ + return *hdr ? -1 : type; +} + +int parse_sha1_header(const char *hdr, unsigned long *sizep) +{ + struct object_info oi = OBJECT_INFO_INIT; + + oi.sizep = sizep; + return parse_sha1_header_extended(hdr, &oi, 0); +} + +static int sha1_loose_object_info(struct repository *r, + const unsigned char *sha1, + struct object_info *oi, int flags) +{ + int status = 0; + unsigned long mapsize; + void *map; + git_zstream stream; + char hdr[MAX_HEADER_LEN]; + struct strbuf hdrbuf = STRBUF_INIT; + unsigned long size_scratch; + + if (oi->delta_base_sha1) + hashclr(oi->delta_base_sha1); + + /* + * If we don't care about type or size, then we don't + * need to look inside the object at all. Note that we + * do not optimize out the stat call, even if the + * caller doesn't care about the disk-size, since our + * return value implicitly indicates whether the + * object even exists. + */ + if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) { + const char *path; + struct stat st; + if (stat_sha1_file(r, sha1, &st, &path) < 0) + return -1; + if (oi->disk_sizep) + *oi->disk_sizep = st.st_size; + return 0; + } + + map = map_sha1_file(r, sha1, &mapsize); + if (!map) + return -1; + + if (!oi->sizep) + oi->sizep = &size_scratch; + + if (oi->disk_sizep) + *oi->disk_sizep = mapsize; + if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) { + if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0) + status = error("unable to unpack %s header with --allow-unknown-type", + sha1_to_hex(sha1)); + } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) + status = error("unable to unpack %s header", + sha1_to_hex(sha1)); + if (status < 0) + ; /* Do nothing */ + else if (hdrbuf.len) { + if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0) + status = error("unable to parse %s header with --allow-unknown-type", + sha1_to_hex(sha1)); + } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0) + status = error("unable to parse %s header", sha1_to_hex(sha1)); + + if (status >= 0 && oi->contentp) { + *oi->contentp = unpack_sha1_rest(&stream, hdr, + *oi->sizep, sha1); + if (!*oi->contentp) { + git_inflate_end(&stream); + status = -1; + } + } else + git_inflate_end(&stream); + + munmap(map, mapsize); + if (status && oi->typep) + *oi->typep = status; + if (oi->sizep == &size_scratch) + oi->sizep = NULL; + strbuf_release(&hdrbuf); + oi->whence = OI_LOOSE; + return (status < 0) ? status : 0; +} + +int fetch_if_missing = 1; + +int oid_object_info_extended(const struct object_id *oid, struct object_info *oi, unsigned flags) +{ + static struct object_info blank_oi = OBJECT_INFO_INIT; + struct pack_entry e; + int rtype; + const struct object_id *real = oid; + int already_retried = 0; + + if (flags & OBJECT_INFO_LOOKUP_REPLACE) + real = lookup_replace_object(oid); + + if (is_null_oid(real)) + return -1; + + if (!oi) + oi = &blank_oi; + + if (!(flags & OBJECT_INFO_SKIP_CACHED)) { + struct cached_object *co = find_cached_object(real->hash); + if (co) { + if (oi->typep) + *(oi->typep) = co->type; + if (oi->sizep) + *(oi->sizep) = co->size; + if (oi->disk_sizep) + *(oi->disk_sizep) = 0; + if (oi->delta_base_sha1) + hashclr(oi->delta_base_sha1); + if (oi->type_name) + strbuf_addstr(oi->type_name, type_name(co->type)); + if (oi->contentp) + *oi->contentp = xmemdupz(co->buf, co->size); + oi->whence = OI_CACHED; + return 0; + } + } + + while (1) { + if (find_pack_entry(the_repository, real->hash, &e)) + break; + + if (flags & OBJECT_INFO_IGNORE_LOOSE) + return -1; + + /* Most likely it's a loose object. */ + if (!sha1_loose_object_info(the_repository, real->hash, oi, flags)) + return 0; + + /* Not a loose object; someone else may have just packed it. */ + if (!(flags & OBJECT_INFO_QUICK)) { + reprepare_packed_git(the_repository); + if (find_pack_entry(the_repository, real->hash, &e)) + break; + } + + /* Check if it is a missing object */ + if (fetch_if_missing && repository_format_partial_clone && + !already_retried) { + /* + * TODO Investigate haveing fetch_object() return + * TODO error/success and stopping the music here. + */ + fetch_object(repository_format_partial_clone, real->hash); + already_retried = 1; + continue; + } + + return -1; + } + + if (oi == &blank_oi) + /* + * We know that the caller doesn't actually need the + * information below, so return early. + */ + return 0; + rtype = packed_object_info(e.p, e.offset, oi); + if (rtype < 0) { + mark_bad_packed_object(e.p, real->hash); + return oid_object_info_extended(real, oi, 0); + } else if (oi->whence == OI_PACKED) { + oi->u.packed.offset = e.offset; + oi->u.packed.pack = e.p; + oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA || + rtype == OBJ_OFS_DELTA); + } + + return 0; +} + +/* returns enum object_type or negative */ +int oid_object_info(const struct object_id *oid, unsigned long *sizep) +{ + enum object_type type; + struct object_info oi = OBJECT_INFO_INIT; + + oi.typep = &type; + oi.sizep = sizep; + if (oid_object_info_extended(oid, &oi, + OBJECT_INFO_LOOKUP_REPLACE) < 0) + return -1; + return type; +} + +static void *read_object(const unsigned char *sha1, enum object_type *type, + unsigned long *size) +{ + struct object_id oid; + struct object_info oi = OBJECT_INFO_INIT; + void *content; + oi.typep = type; + oi.sizep = size; + oi.contentp = &content; + + hashcpy(oid.hash, sha1); + + if (oid_object_info_extended(&oid, &oi, 0) < 0) + return NULL; + return content; +} + +int pretend_object_file(void *buf, unsigned long len, enum object_type type, + struct object_id *oid) +{ + struct cached_object *co; + + hash_object_file(buf, len, type_name(type), oid); + if (has_sha1_file(oid->hash) || find_cached_object(oid->hash)) + return 0; + ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc); + co = &cached_objects[cached_object_nr++]; + co->size = len; + co->type = type; + co->buf = xmalloc(len); + memcpy(co->buf, buf, len); + hashcpy(co->sha1, oid->hash); + return 0; +} + +/* + * This function dies on corrupt objects; the callers who want to + * deal with them should arrange to call read_object() and give error + * messages themselves. + */ +void *read_object_file_extended(const struct object_id *oid, + enum object_type *type, + unsigned long *size, + int lookup_replace) +{ + void *data; + const struct packed_git *p; + const char *path; + struct stat st; + const struct object_id *repl = lookup_replace ? lookup_replace_object(oid) + : oid; + + errno = 0; + data = read_object(repl->hash, type, size); + if (data) + return data; + + if (errno && errno != ENOENT) + die_errno("failed to read object %s", oid_to_hex(oid)); + + /* die if we replaced an object with one that does not exist */ + if (repl != oid) + die("replacement %s not found for %s", + oid_to_hex(repl), oid_to_hex(oid)); + + if (!stat_sha1_file(the_repository, repl->hash, &st, &path)) + die("loose object %s (stored in %s) is corrupt", + oid_to_hex(repl), path); + + if ((p = has_packed_and_bad(repl->hash)) != NULL) + die("packed object %s (stored in %s) is corrupt", + oid_to_hex(repl), p->pack_name); + + return NULL; +} + +void *read_object_with_reference(const struct object_id *oid, + const char *required_type_name, + unsigned long *size, + struct object_id *actual_oid_return) +{ + enum object_type type, required_type; + void *buffer; + unsigned long isize; + struct object_id actual_oid; + + required_type = type_from_string(required_type_name); + oidcpy(&actual_oid, oid); + while (1) { + int ref_length = -1; + const char *ref_type = NULL; + + buffer = read_object_file(&actual_oid, &type, &isize); + if (!buffer) + return NULL; + if (type == required_type) { + *size = isize; + if (actual_oid_return) + oidcpy(actual_oid_return, &actual_oid); + return buffer; + } + /* Handle references */ + else if (type == OBJ_COMMIT) + ref_type = "tree "; + else if (type == OBJ_TAG) + ref_type = "object "; + else { + free(buffer); + return NULL; + } + ref_length = strlen(ref_type); + + if (ref_length + GIT_SHA1_HEXSZ > isize || + memcmp(buffer, ref_type, ref_length) || + get_oid_hex((char *) buffer + ref_length, &actual_oid)) { + free(buffer); + return NULL; + } + free(buffer); + /* Now we have the ID of the referred-to object in + * actual_oid. Check again. */ + } +} + +static void write_object_file_prepare(const void *buf, unsigned long len, + const char *type, struct object_id *oid, + char *hdr, int *hdrlen) +{ + git_hash_ctx c; + + /* Generate the header */ + *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1; + + /* Sha1.. */ + the_hash_algo->init_fn(&c); + the_hash_algo->update_fn(&c, hdr, *hdrlen); + the_hash_algo->update_fn(&c, buf, len); + the_hash_algo->final_fn(oid->hash, &c); +} + +/* + * Move the just written object into its final resting place. + */ +int finalize_object_file(const char *tmpfile, const char *filename) +{ + int ret = 0; + + if (object_creation_mode == OBJECT_CREATION_USES_RENAMES) + goto try_rename; + else if (link(tmpfile, filename)) + ret = errno; + + /* + * Coda hack - coda doesn't like cross-directory links, + * so we fall back to a rename, which will mean that it + * won't be able to check collisions, but that's not a + * big deal. + * + * The same holds for FAT formatted media. + * + * When this succeeds, we just return. We have nothing + * left to unlink. + */ + if (ret && ret != EEXIST) { + try_rename: + if (!rename(tmpfile, filename)) + goto out; + ret = errno; + } + unlink_or_warn(tmpfile); + if (ret) { + if (ret != EEXIST) { + return error_errno("unable to write sha1 filename %s", filename); + } + /* FIXME!!! Collision check here ? */ + } + +out: + if (adjust_shared_perm(filename)) + return error("unable to set permission to '%s'", filename); + return 0; +} + +static int write_buffer(int fd, const void *buf, size_t len) +{ + if (write_in_full(fd, buf, len) < 0) + return error_errno("file write error"); + return 0; +} + +int hash_object_file(const void *buf, unsigned long len, const char *type, + struct object_id *oid) +{ + char hdr[MAX_HEADER_LEN]; + int hdrlen = sizeof(hdr); + write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen); + return 0; +} + +/* Finalize a file on disk, and close it. */ +static void close_sha1_file(int fd) +{ + if (fsync_object_files) + fsync_or_die(fd, "sha1 file"); + if (close(fd) != 0) + die_errno("error when closing sha1 file"); +} + +/* Size of directory component, including the ending '/' */ +static inline int directory_size(const char *filename) +{ + const char *s = strrchr(filename, '/'); + if (!s) + return 0; + return s - filename + 1; +} + +/* + * This creates a temporary file in the same directory as the final + * 'filename' + * + * We want to avoid cross-directory filename renames, because those + * can have problems on various filesystems (FAT, NFS, Coda). + */ +static int create_tmpfile(struct strbuf *tmp, const char *filename) +{ + int fd, dirlen = directory_size(filename); + + strbuf_reset(tmp); + strbuf_add(tmp, filename, dirlen); + strbuf_addstr(tmp, "tmp_obj_XXXXXX"); + fd = git_mkstemp_mode(tmp->buf, 0444); + if (fd < 0 && dirlen && errno == ENOENT) { + /* + * Make sure the directory exists; note that the contents + * of the buffer are undefined after mkstemp returns an + * error, so we have to rewrite the whole buffer from + * scratch. + */ + strbuf_reset(tmp); + strbuf_add(tmp, filename, dirlen - 1); + if (mkdir(tmp->buf, 0777) && errno != EEXIST) + return -1; + if (adjust_shared_perm(tmp->buf)) + return -1; + + /* Try again */ + strbuf_addstr(tmp, "/tmp_obj_XXXXXX"); + fd = git_mkstemp_mode(tmp->buf, 0444); + } + return fd; +} + +static int write_loose_object(const struct object_id *oid, char *hdr, + int hdrlen, const void *buf, unsigned long len, + time_t mtime) +{ + int fd, ret; + unsigned char compressed[4096]; + git_zstream stream; + git_hash_ctx c; + struct object_id parano_oid; + static struct strbuf tmp_file = STRBUF_INIT; + static struct strbuf filename = STRBUF_INIT; + + strbuf_reset(&filename); + sha1_file_name(the_repository, &filename, oid->hash); + + fd = create_tmpfile(&tmp_file, filename.buf); + if (fd < 0) { + if (errno == EACCES) + return error("insufficient permission for adding an object to repository database %s", get_object_directory()); + else + return error_errno("unable to create temporary file"); + } + + /* Set it up */ + git_deflate_init(&stream, zlib_compression_level); + stream.next_out = compressed; + stream.avail_out = sizeof(compressed); + the_hash_algo->init_fn(&c); + + /* First header.. */ + stream.next_in = (unsigned char *)hdr; + stream.avail_in = hdrlen; + while (git_deflate(&stream, 0) == Z_OK) + ; /* nothing */ + the_hash_algo->update_fn(&c, hdr, hdrlen); + + /* Then the data itself.. */ + stream.next_in = (void *)buf; + stream.avail_in = len; + do { + unsigned char *in0 = stream.next_in; + ret = git_deflate(&stream, Z_FINISH); + the_hash_algo->update_fn(&c, in0, stream.next_in - in0); + if (write_buffer(fd, compressed, stream.next_out - compressed) < 0) + die("unable to write sha1 file"); + stream.next_out = compressed; + stream.avail_out = sizeof(compressed); + } while (ret == Z_OK); + + if (ret != Z_STREAM_END) + die("unable to deflate new object %s (%d)", oid_to_hex(oid), + ret); + ret = git_deflate_end_gently(&stream); + if (ret != Z_OK) + die("deflateEnd on object %s failed (%d)", oid_to_hex(oid), + ret); + the_hash_algo->final_fn(parano_oid.hash, &c); + if (oidcmp(oid, ¶no_oid) != 0) + die("confused by unstable object source data for %s", + oid_to_hex(oid)); + + close_sha1_file(fd); + + if (mtime) { + struct utimbuf utb; + utb.actime = mtime; + utb.modtime = mtime; + if (utime(tmp_file.buf, &utb) < 0) + warning_errno("failed utime() on %s", tmp_file.buf); + } + + return finalize_object_file(tmp_file.buf, filename.buf); +} + +static int freshen_loose_object(const unsigned char *sha1) +{ + return check_and_freshen(sha1, 1); +} + +static int freshen_packed_object(const unsigned char *sha1) +{ + struct pack_entry e; + if (!find_pack_entry(the_repository, sha1, &e)) + return 0; + if (e.p->freshened) + return 1; + if (!freshen_file(e.p->pack_name)) + return 0; + e.p->freshened = 1; + return 1; +} + +int write_object_file(const void *buf, unsigned long len, const char *type, + struct object_id *oid) +{ + char hdr[MAX_HEADER_LEN]; + int hdrlen = sizeof(hdr); + + /* Normally if we have it in the pack then we do not bother writing + * it out into .git/objects/??/?{38} file. + */ + write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen); + if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) + return 0; + return write_loose_object(oid, hdr, hdrlen, buf, len, 0); +} + +int hash_object_file_literally(const void *buf, unsigned long len, + const char *type, struct object_id *oid, + unsigned flags) +{ + char *header; + int hdrlen, status = 0; + + /* type string, SP, %lu of the length plus NUL must fit this */ + hdrlen = strlen(type) + MAX_HEADER_LEN; + header = xmalloc(hdrlen); + write_object_file_prepare(buf, len, type, oid, header, &hdrlen); + + if (!(flags & HASH_WRITE_OBJECT)) + goto cleanup; + if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) + goto cleanup; + status = write_loose_object(oid, header, hdrlen, buf, len, 0); + +cleanup: + free(header); + return status; +} + +int force_object_loose(const struct object_id *oid, time_t mtime) +{ + void *buf; + unsigned long len; + enum object_type type; + char hdr[MAX_HEADER_LEN]; + int hdrlen; + int ret; + + if (has_loose_object(oid->hash)) + return 0; + buf = read_object(oid->hash, &type, &len); + if (!buf) + return error("cannot read sha1_file for %s", oid_to_hex(oid)); + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), len) + 1; + ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime); + free(buf); + + return ret; +} + +int has_sha1_file_with_flags(const unsigned char *sha1, int flags) +{ + struct object_id oid; + if (!startup_info->have_repository) + return 0; + hashcpy(oid.hash, sha1); + return oid_object_info_extended(&oid, NULL, + flags | OBJECT_INFO_SKIP_CACHED) >= 0; +} + +int has_object_file(const struct object_id *oid) +{ + return has_sha1_file(oid->hash); +} + +int has_object_file_with_flags(const struct object_id *oid, int flags) +{ + return has_sha1_file_with_flags(oid->hash, flags); +} + +static void check_tree(const void *buf, size_t size) +{ + struct tree_desc desc; + struct name_entry entry; + + init_tree_desc(&desc, buf, size); + while (tree_entry(&desc, &entry)) + /* do nothing + * tree_entry() will die() on malformed entries */ + ; +} + +static void check_commit(const void *buf, size_t size) +{ + struct commit c; + memset(&c, 0, sizeof(c)); + if (parse_commit_buffer(&c, buf, size)) + die("corrupt commit"); +} + +static void check_tag(const void *buf, size_t size) +{ + struct tag t; + memset(&t, 0, sizeof(t)); + if (parse_tag_buffer(&t, buf, size)) + die("corrupt tag"); +} + +static int index_mem(struct object_id *oid, void *buf, size_t size, + enum object_type type, + const char *path, unsigned flags) +{ + int ret, re_allocated = 0; + int write_object = flags & HASH_WRITE_OBJECT; + + if (!type) + type = OBJ_BLOB; + + /* + * Convert blobs to git internal format + */ + if ((type == OBJ_BLOB) && path) { + struct strbuf nbuf = STRBUF_INIT; + if (convert_to_git(&the_index, path, buf, size, &nbuf, + get_conv_flags(flags))) { + buf = strbuf_detach(&nbuf, &size); + re_allocated = 1; + } + } + if (flags & HASH_FORMAT_CHECK) { + if (type == OBJ_TREE) + check_tree(buf, size); + if (type == OBJ_COMMIT) + check_commit(buf, size); + if (type == OBJ_TAG) + check_tag(buf, size); + } + + if (write_object) + ret = write_object_file(buf, size, type_name(type), oid); + else + ret = hash_object_file(buf, size, type_name(type), oid); + if (re_allocated) + free(buf); + return ret; +} + +static int index_stream_convert_blob(struct object_id *oid, int fd, + const char *path, unsigned flags) +{ + int ret; + const int write_object = flags & HASH_WRITE_OBJECT; + struct strbuf sbuf = STRBUF_INIT; + + assert(path); + assert(would_convert_to_git_filter_fd(path)); + + convert_to_git_filter_fd(&the_index, path, fd, &sbuf, + get_conv_flags(flags)); + + if (write_object) + ret = write_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB), + oid); + else + ret = hash_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB), + oid); + strbuf_release(&sbuf); + return ret; +} + +static int index_pipe(struct object_id *oid, int fd, enum object_type type, + const char *path, unsigned flags) +{ + struct strbuf sbuf = STRBUF_INIT; + int ret; + + if (strbuf_read(&sbuf, fd, 4096) >= 0) + ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags); + else + ret = -1; + strbuf_release(&sbuf); + return ret; +} + +#define SMALL_FILE_SIZE (32*1024) + +static int index_core(struct object_id *oid, int fd, size_t size, + enum object_type type, const char *path, + unsigned flags) +{ + int ret; + + if (!size) { + ret = index_mem(oid, "", size, type, path, flags); + } else if (size <= SMALL_FILE_SIZE) { + char *buf = xmalloc(size); + ssize_t read_result = read_in_full(fd, buf, size); + if (read_result < 0) + ret = error_errno("read error while indexing %s", + path ? path : ""); + else if (read_result != size) + ret = error("short read while indexing %s", + path ? path : ""); + else + ret = index_mem(oid, buf, size, type, path, flags); + free(buf); + } else { + void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + ret = index_mem(oid, buf, size, type, path, flags); + munmap(buf, size); + } + return ret; +} + +/* + * This creates one packfile per large blob unless bulk-checkin + * machinery is "plugged". + * + * This also bypasses the usual "convert-to-git" dance, and that is on + * purpose. We could write a streaming version of the converting + * functions and insert that before feeding the data to fast-import + * (or equivalent in-core API described above). However, that is + * somewhat complicated, as we do not know the size of the filter + * result, which we need to know beforehand when writing a git object. + * Since the primary motivation for trying to stream from the working + * tree file and to avoid mmaping it in core is to deal with large + * binary blobs, they generally do not want to get any conversion, and + * callers should avoid this code path when filters are requested. + */ +static int index_stream(struct object_id *oid, int fd, size_t size, + enum object_type type, const char *path, + unsigned flags) +{ + return index_bulk_checkin(oid, fd, size, type, path, flags); +} + +int index_fd(struct object_id *oid, int fd, struct stat *st, + enum object_type type, const char *path, unsigned flags) +{ + int ret; + + /* + * Call xsize_t() only when needed to avoid potentially unnecessary + * die() for large files. + */ + if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path)) + ret = index_stream_convert_blob(oid, fd, path, flags); + else if (!S_ISREG(st->st_mode)) + ret = index_pipe(oid, fd, type, path, flags); + else if (st->st_size <= big_file_threshold || type != OBJ_BLOB || + (path && would_convert_to_git(&the_index, path))) + ret = index_core(oid, fd, xsize_t(st->st_size), type, path, + flags); + else + ret = index_stream(oid, fd, xsize_t(st->st_size), type, path, + flags); + close(fd); + return ret; +} + +int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags) +{ + int fd; + struct strbuf sb = STRBUF_INIT; + int rc = 0; + + switch (st->st_mode & S_IFMT) { + case S_IFREG: + fd = open(path, O_RDONLY); + if (fd < 0) + return error_errno("open(\"%s\")", path); + if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0) + return error("%s: failed to insert into database", + path); + break; + case S_IFLNK: + if (strbuf_readlink(&sb, path, st->st_size)) + return error_errno("readlink(\"%s\")", path); + if (!(flags & HASH_WRITE_OBJECT)) + hash_object_file(sb.buf, sb.len, blob_type, oid); + else if (write_object_file(sb.buf, sb.len, blob_type, oid)) + rc = error("%s: failed to insert into database", path); + strbuf_release(&sb); + break; + case S_IFDIR: + return resolve_gitlink_ref(path, "HEAD", oid); + default: + return error("%s: unsupported file type", path); + } + return rc; +} + +int read_pack_header(int fd, struct pack_header *header) +{ + if (read_in_full(fd, header, sizeof(*header)) != sizeof(*header)) + /* "eof before pack header was fully read" */ + return PH_ERROR_EOF; + + if (header->hdr_signature != htonl(PACK_SIGNATURE)) + /* "protocol error (pack signature mismatch detected)" */ + return PH_ERROR_PACK_SIGNATURE; + if (!pack_version_ok(header->hdr_version)) + /* "protocol error (pack version unsupported)" */ + return PH_ERROR_PROTOCOL; + return 0; +} + +void assert_oid_type(const struct object_id *oid, enum object_type expect) +{ + enum object_type type = oid_object_info(oid, NULL); + if (type < 0) + die("%s is not a valid object", oid_to_hex(oid)); + if (type != expect) + die("%s is not a valid '%s' object", oid_to_hex(oid), + type_name(expect)); +} + +int for_each_file_in_obj_subdir(unsigned int subdir_nr, + struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + size_t origlen, baselen; + DIR *dir; + struct dirent *de; + int r = 0; + struct object_id oid; + + if (subdir_nr > 0xff) + BUG("invalid loose object subdirectory: %x", subdir_nr); + + origlen = path->len; + strbuf_complete(path, '/'); + strbuf_addf(path, "%02x", subdir_nr); + + dir = opendir(path->buf); + if (!dir) { + if (errno != ENOENT) + r = error_errno("unable to open %s", path->buf); + strbuf_setlen(path, origlen); + return r; + } + + oid.hash[0] = subdir_nr; + strbuf_addch(path, '/'); + baselen = path->len; + + while ((de = readdir(dir))) { + size_t namelen; + if (is_dot_or_dotdot(de->d_name)) + continue; + + namelen = strlen(de->d_name); + strbuf_setlen(path, baselen); + strbuf_add(path, de->d_name, namelen); + if (namelen == GIT_SHA1_HEXSZ - 2 && + !hex_to_bytes(oid.hash + 1, de->d_name, + GIT_SHA1_RAWSZ - 1)) { + if (obj_cb) { + r = obj_cb(&oid, path->buf, data); + if (r) + break; + } + continue; + } + + if (cruft_cb) { + r = cruft_cb(de->d_name, path->buf, data); + if (r) + break; + } + } + closedir(dir); + + strbuf_setlen(path, baselen - 1); + if (!r && subdir_cb) + r = subdir_cb(subdir_nr, path->buf, data); + + strbuf_setlen(path, origlen); + + return r; +} + +int for_each_loose_file_in_objdir_buf(struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + int r = 0; + int i; + + for (i = 0; i < 256; i++) { + r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb, + subdir_cb, data); + if (r) + break; + } + + return r; +} + +int for_each_loose_file_in_objdir(const char *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + struct strbuf buf = STRBUF_INIT; + int r; + + strbuf_addstr(&buf, path); + r = for_each_loose_file_in_objdir_buf(&buf, obj_cb, cruft_cb, + subdir_cb, data); + strbuf_release(&buf); + + return r; +} + +struct loose_alt_odb_data { + each_loose_object_fn *cb; + void *data; +}; + +static int loose_from_alt_odb(struct alternate_object_database *alt, + void *vdata) +{ + struct loose_alt_odb_data *data = vdata; + struct strbuf buf = STRBUF_INIT; + int r; + + strbuf_addstr(&buf, alt->path); + r = for_each_loose_file_in_objdir_buf(&buf, + data->cb, NULL, NULL, + data->data); + strbuf_release(&buf); + return r; +} + +int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags) +{ + struct loose_alt_odb_data alt; + int r; + + r = for_each_loose_file_in_objdir(get_object_directory(), + cb, NULL, NULL, data); + if (r) + return r; + + if (flags & FOR_EACH_OBJECT_LOCAL_ONLY) + return 0; + + alt.cb = cb; + alt.data = data; + return foreach_alt_odb(loose_from_alt_odb, &alt); +} + +static int check_stream_sha1(git_zstream *stream, + const char *hdr, + unsigned long size, + const char *path, + const unsigned char *expected_sha1) +{ + git_hash_ctx c; + unsigned char real_sha1[GIT_MAX_RAWSZ]; + unsigned char buf[4096]; + unsigned long total_read; + int status = Z_OK; + + the_hash_algo->init_fn(&c); + the_hash_algo->update_fn(&c, hdr, stream->total_out); + + /* + * We already read some bytes into hdr, but the ones up to the NUL + * do not count against the object's content size. + */ + total_read = stream->total_out - strlen(hdr) - 1; + + /* + * This size comparison must be "<=" to read the final zlib packets; + * see the comment in unpack_sha1_rest for details. + */ + while (total_read <= size && + (status == Z_OK || status == Z_BUF_ERROR)) { + stream->next_out = buf; + stream->avail_out = sizeof(buf); + if (size - total_read < stream->avail_out) + stream->avail_out = size - total_read; + status = git_inflate(stream, Z_FINISH); + the_hash_algo->update_fn(&c, buf, stream->next_out - buf); + total_read += stream->next_out - buf; + } + git_inflate_end(stream); + + if (status != Z_STREAM_END) { + error("corrupt loose object '%s'", sha1_to_hex(expected_sha1)); + return -1; + } + if (stream->avail_in) { + error("garbage at end of loose object '%s'", + sha1_to_hex(expected_sha1)); + return -1; + } + + the_hash_algo->final_fn(real_sha1, &c); + if (hashcmp(expected_sha1, real_sha1)) { + error("sha1 mismatch for %s (expected %s)", path, + sha1_to_hex(expected_sha1)); + return -1; + } + + return 0; +} + +int read_loose_object(const char *path, + const struct object_id *expected_oid, + enum object_type *type, + unsigned long *size, + void **contents) +{ + int ret = -1; + void *map = NULL; + unsigned long mapsize; + git_zstream stream; + char hdr[MAX_HEADER_LEN]; + + *contents = NULL; + + map = map_sha1_file_1(the_repository, path, NULL, &mapsize); + if (!map) { + error_errno("unable to mmap %s", path); + goto out; + } + + if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) { + error("unable to unpack header of %s", path); + goto out; + } + + *type = parse_sha1_header(hdr, size); + if (*type < 0) { + error("unable to parse header of %s", path); + git_inflate_end(&stream); + goto out; + } + + if (*type == OBJ_BLOB) { + if (check_stream_sha1(&stream, hdr, *size, path, expected_oid->hash) < 0) + goto out; + } else { + *contents = unpack_sha1_rest(&stream, hdr, *size, expected_oid->hash); + if (!*contents) { + error("unable to unpack contents of %s", path); + git_inflate_end(&stream); + goto out; + } + if (check_object_signature(expected_oid, *contents, + *size, type_name(*type))) { + error("sha1 mismatch for %s (expected %s)", path, + oid_to_hex(expected_oid)); + free(*contents); + goto out; + } + } + + ret = 0; /* everything checks out */ + +out: + if (map) + munmap(map, mapsize); + return ret; +} diff --git a/sha1_file.c b/sha1_file.c deleted file mode 100644 index 77ccaab928..0000000000 --- a/sha1_file.c +++ /dev/null @@ -1,2254 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - * - * This handles basic git sha1 object files - packing, unpacking, - * creation etc. - */ -#include "cache.h" -#include "config.h" -#include "string-list.h" -#include "lockfile.h" -#include "delta.h" -#include "pack.h" -#include "blob.h" -#include "commit.h" -#include "run-command.h" -#include "tag.h" -#include "tree.h" -#include "tree-walk.h" -#include "refs.h" -#include "pack-revindex.h" -#include "sha1-lookup.h" -#include "bulk-checkin.h" -#include "repository.h" -#include "streaming.h" -#include "dir.h" -#include "list.h" -#include "mergesort.h" -#include "quote.h" -#include "packfile.h" -#include "fetch-object.h" -#include "object-store.h" - -/* The maximum size for an object header. */ -#define MAX_HEADER_LEN 32 - -const unsigned char null_sha1[GIT_MAX_RAWSZ]; -const struct object_id null_oid; -const struct object_id empty_tree_oid = { - EMPTY_TREE_SHA1_BIN_LITERAL -}; -const struct object_id empty_blob_oid = { - EMPTY_BLOB_SHA1_BIN_LITERAL -}; - -static void git_hash_sha1_init(git_hash_ctx *ctx) -{ - git_SHA1_Init(&ctx->sha1); -} - -static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len) -{ - git_SHA1_Update(&ctx->sha1, data, len); -} - -static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx) -{ - git_SHA1_Final(hash, &ctx->sha1); -} - -static void git_hash_unknown_init(git_hash_ctx *ctx) -{ - die("trying to init unknown hash"); -} - -static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len) -{ - die("trying to update unknown hash"); -} - -static void git_hash_unknown_final(unsigned char *hash, git_hash_ctx *ctx) -{ - die("trying to finalize unknown hash"); -} - -const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { - { - NULL, - 0x00000000, - 0, - 0, - git_hash_unknown_init, - git_hash_unknown_update, - git_hash_unknown_final, - NULL, - NULL, - }, - { - "sha-1", - /* "sha1", big-endian */ - 0x73686131, - GIT_SHA1_RAWSZ, - GIT_SHA1_HEXSZ, - git_hash_sha1_init, - git_hash_sha1_update, - git_hash_sha1_final, - &empty_tree_oid, - &empty_blob_oid, - }, -}; - -/* - * This is meant to hold a *small* number of objects that you would - * want read_sha1_file() to be able to return, but yet you do not want - * to write them into the object store (e.g. a browse-only - * application). - */ -static struct cached_object { - unsigned char sha1[20]; - enum object_type type; - void *buf; - unsigned long size; -} *cached_objects; -static int cached_object_nr, cached_object_alloc; - -static struct cached_object empty_tree = { - EMPTY_TREE_SHA1_BIN_LITERAL, - OBJ_TREE, - "", - 0 -}; - -static struct cached_object *find_cached_object(const unsigned char *sha1) -{ - int i; - struct cached_object *co = cached_objects; - - for (i = 0; i < cached_object_nr; i++, co++) { - if (!hashcmp(co->sha1, sha1)) - return co; - } - if (!hashcmp(sha1, empty_tree.sha1)) - return &empty_tree; - return NULL; -} - - -static int get_conv_flags(unsigned flags) -{ - if (flags & HASH_RENORMALIZE) - return CONV_EOL_RENORMALIZE; - else if (flags & HASH_WRITE_OBJECT) - return global_conv_flags_eol; - else - return 0; -} - - -int mkdir_in_gitdir(const char *path) -{ - if (mkdir(path, 0777)) { - int saved_errno = errno; - struct stat st; - struct strbuf sb = STRBUF_INIT; - - if (errno != EEXIST) - return -1; - /* - * Are we looking at a path in a symlinked worktree - * whose original repository does not yet have it? - * e.g. .git/rr-cache pointing at its original - * repository in which the user hasn't performed any - * conflict resolution yet? - */ - if (lstat(path, &st) || !S_ISLNK(st.st_mode) || - strbuf_readlink(&sb, path, st.st_size) || - !is_absolute_path(sb.buf) || - mkdir(sb.buf, 0777)) { - strbuf_release(&sb); - errno = saved_errno; - return -1; - } - strbuf_release(&sb); - } - return adjust_shared_perm(path); -} - -enum scld_error safe_create_leading_directories(char *path) -{ - char *next_component = path + offset_1st_component(path); - enum scld_error ret = SCLD_OK; - - while (ret == SCLD_OK && next_component) { - struct stat st; - char *slash = next_component, slash_character; - - while (*slash && !is_dir_sep(*slash)) - slash++; - - if (!*slash) - break; - - next_component = slash + 1; - while (is_dir_sep(*next_component)) - next_component++; - if (!*next_component) - break; - - slash_character = *slash; - *slash = '\0'; - if (!stat(path, &st)) { - /* path exists */ - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - ret = SCLD_EXISTS; - } - } else if (mkdir(path, 0777)) { - if (errno == EEXIST && - !stat(path, &st) && S_ISDIR(st.st_mode)) - ; /* somebody created it since we checked */ - else if (errno == ENOENT) - /* - * Either mkdir() failed because - * somebody just pruned the containing - * directory, or stat() failed because - * the file that was in our way was - * just removed. Either way, inform - * the caller that it might be worth - * trying again: - */ - ret = SCLD_VANISHED; - else - ret = SCLD_FAILED; - } else if (adjust_shared_perm(path)) { - ret = SCLD_PERMS; - } - *slash = slash_character; - } - return ret; -} - -enum scld_error safe_create_leading_directories_const(const char *path) -{ - int save_errno; - /* path points to cache entries, so xstrdup before messing with it */ - char *buf = xstrdup(path); - enum scld_error result = safe_create_leading_directories(buf); - - save_errno = errno; - free(buf); - errno = save_errno; - return result; -} - -int raceproof_create_file(const char *path, create_file_fn fn, void *cb) -{ - /* - * The number of times we will try to remove empty directories - * in the way of path. This is only 1 because if another - * process is racily creating directories that conflict with - * us, we don't want to fight against them. - */ - int remove_directories_remaining = 1; - - /* - * The number of times that we will try to create the - * directories containing path. We are willing to attempt this - * more than once, because another process could be trying to - * clean up empty directories at the same time as we are - * trying to create them. - */ - int create_directories_remaining = 3; - - /* A scratch copy of path, filled lazily if we need it: */ - struct strbuf path_copy = STRBUF_INIT; - - int ret, save_errno; - - /* Sanity check: */ - assert(*path); - -retry_fn: - ret = fn(path, cb); - save_errno = errno; - if (!ret) - goto out; - - if (errno == EISDIR && remove_directories_remaining-- > 0) { - /* - * A directory is in the way. Maybe it is empty; try - * to remove it: - */ - if (!path_copy.len) - strbuf_addstr(&path_copy, path); - - if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY)) - goto retry_fn; - } else if (errno == ENOENT && create_directories_remaining-- > 0) { - /* - * Maybe the containing directory didn't exist, or - * maybe it was just deleted by a process that is - * racing with us to clean up empty directories. Try - * to create it: - */ - enum scld_error scld_result; - - if (!path_copy.len) - strbuf_addstr(&path_copy, path); - - do { - scld_result = safe_create_leading_directories(path_copy.buf); - if (scld_result == SCLD_OK) - goto retry_fn; - } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0); - } - -out: - strbuf_release(&path_copy); - errno = save_errno; - return ret; -} - -static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1) -{ - int i; - for (i = 0; i < 20; i++) { - static char hex[] = "0123456789abcdef"; - unsigned int val = sha1[i]; - strbuf_addch(buf, hex[val >> 4]); - strbuf_addch(buf, hex[val & 0xf]); - if (!i) - strbuf_addch(buf, '/'); - } -} - -void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned char *sha1) -{ - strbuf_addstr(buf, r->objects->objectdir); - strbuf_addch(buf, '/'); - fill_sha1_path(buf, sha1); -} - -struct strbuf *alt_scratch_buf(struct alternate_object_database *alt) -{ - strbuf_setlen(&alt->scratch, alt->base_len); - return &alt->scratch; -} - -static const char *alt_sha1_path(struct alternate_object_database *alt, - const unsigned char *sha1) -{ - struct strbuf *buf = alt_scratch_buf(alt); - fill_sha1_path(buf, sha1); - return buf->buf; -} - -/* - * Return non-zero iff the path is usable as an alternate object database. - */ -static int alt_odb_usable(struct raw_object_store *o, - struct strbuf *path, - const char *normalized_objdir) -{ - struct alternate_object_database *alt; - - /* Detect cases where alternate disappeared */ - if (!is_directory(path->buf)) { - error("object directory %s does not exist; " - "check .git/objects/info/alternates.", - path->buf); - return 0; - } - - /* - * Prevent the common mistake of listing the same - * thing twice, or object directory itself. - */ - for (alt = o->alt_odb_list; alt; alt = alt->next) { - if (!fspathcmp(path->buf, alt->path)) - return 0; - } - if (!fspathcmp(path->buf, normalized_objdir)) - return 0; - - return 1; -} - -/* - * Prepare alternate object database registry. - * - * The variable alt_odb_list points at the list of struct - * alternate_object_database. The elements on this list come from - * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT - * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, - * whose contents is similar to that environment variable but can be - * LF separated. Its base points at a statically allocated buffer that - * contains "/the/directory/corresponding/to/.git/objects/...", while - * its name points just after the slash at the end of ".git/objects/" - * in the example above, and has enough space to hold 40-byte hex - * SHA1, an extra slash for the first level indirection, and the - * terminating NUL. - */ -static void read_info_alternates(struct repository *r, - const char *relative_base, - int depth); -static int link_alt_odb_entry(struct repository *r, const char *entry, - const char *relative_base, int depth, const char *normalized_objdir) -{ - struct alternate_object_database *ent; - struct strbuf pathbuf = STRBUF_INIT; - - if (!is_absolute_path(entry) && relative_base) { - strbuf_realpath(&pathbuf, relative_base, 1); - strbuf_addch(&pathbuf, '/'); - } - strbuf_addstr(&pathbuf, entry); - - if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) { - error("unable to normalize alternate object path: %s", - pathbuf.buf); - strbuf_release(&pathbuf); - return -1; - } - - /* - * The trailing slash after the directory name is given by - * this function at the end. Remove duplicates. - */ - while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/') - strbuf_setlen(&pathbuf, pathbuf.len - 1); - - if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir)) { - strbuf_release(&pathbuf); - return -1; - } - - ent = alloc_alt_odb(pathbuf.buf); - - /* add the alternate entry */ - *r->objects->alt_odb_tail = ent; - r->objects->alt_odb_tail = &(ent->next); - ent->next = NULL; - - /* recursively add alternates */ - read_info_alternates(r, pathbuf.buf, depth + 1); - - strbuf_release(&pathbuf); - return 0; -} - -static const char *parse_alt_odb_entry(const char *string, - int sep, - struct strbuf *out) -{ - const char *end; - - strbuf_reset(out); - - if (*string == '#') { - /* comment; consume up to next separator */ - end = strchrnul(string, sep); - } else if (*string == '"' && !unquote_c_style(out, string, &end)) { - /* - * quoted path; unquote_c_style has copied the - * data for us and set "end". Broken quoting (e.g., - * an entry that doesn't end with a quote) falls - * back to the unquoted case below. - */ - } else { - /* normal, unquoted path */ - end = strchrnul(string, sep); - strbuf_add(out, string, end - string); - } - - if (*end) - end++; - return end; -} - -static void link_alt_odb_entries(struct repository *r, const char *alt, - int sep, const char *relative_base, int depth) -{ - struct strbuf objdirbuf = STRBUF_INIT; - struct strbuf entry = STRBUF_INIT; - - if (!alt || !*alt) - return; - - if (depth > 5) { - error("%s: ignoring alternate object stores, nesting too deep.", - relative_base); - return; - } - - strbuf_add_absolute_path(&objdirbuf, r->objects->objectdir); - if (strbuf_normalize_path(&objdirbuf) < 0) - die("unable to normalize object directory: %s", - objdirbuf.buf); - - while (*alt) { - alt = parse_alt_odb_entry(alt, sep, &entry); - if (!entry.len) - continue; - link_alt_odb_entry(r, entry.buf, - relative_base, depth, objdirbuf.buf); - } - strbuf_release(&entry); - strbuf_release(&objdirbuf); -} - -static void read_info_alternates(struct repository *r, - const char *relative_base, - int depth) -{ - char *path; - struct strbuf buf = STRBUF_INIT; - - path = xstrfmt("%s/info/alternates", relative_base); - if (strbuf_read_file(&buf, path, 1024) < 0) { - warn_on_fopen_errors(path); - free(path); - return; - } - - link_alt_odb_entries(r, buf.buf, '\n', relative_base, depth); - strbuf_release(&buf); - free(path); -} - -struct alternate_object_database *alloc_alt_odb(const char *dir) -{ - struct alternate_object_database *ent; - - FLEX_ALLOC_STR(ent, path, dir); - strbuf_init(&ent->scratch, 0); - strbuf_addf(&ent->scratch, "%s/", dir); - ent->base_len = ent->scratch.len; - - return ent; -} - -void add_to_alternates_file(const char *reference) -{ - struct lock_file lock = LOCK_INIT; - char *alts = git_pathdup("objects/info/alternates"); - FILE *in, *out; - int found = 0; - - hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR); - out = fdopen_lock_file(&lock, "w"); - if (!out) - die_errno("unable to fdopen alternates lockfile"); - - in = fopen(alts, "r"); - if (in) { - struct strbuf line = STRBUF_INIT; - - while (strbuf_getline(&line, in) != EOF) { - if (!strcmp(reference, line.buf)) { - found = 1; - break; - } - fprintf_or_die(out, "%s\n", line.buf); - } - - strbuf_release(&line); - fclose(in); - } - else if (errno != ENOENT) - die_errno("unable to read alternates file"); - - if (found) { - rollback_lock_file(&lock); - } else { - fprintf_or_die(out, "%s\n", reference); - if (commit_lock_file(&lock)) - die_errno("unable to move new alternates file into place"); - if (the_repository->objects->alt_odb_tail) - link_alt_odb_entries(the_repository, reference, - '\n', NULL, 0); - } - free(alts); -} - -void add_to_alternates_memory(const char *reference) -{ - /* - * Make sure alternates are initialized, or else our entry may be - * overwritten when they are. - */ - prepare_alt_odb(the_repository); - - link_alt_odb_entries(the_repository, reference, - '\n', NULL, 0); -} - -/* - * Compute the exact path an alternate is at and returns it. In case of - * error NULL is returned and the human readable error is added to `err` - * `path` may be relative and should point to $GITDIR. - * `err` must not be null. - */ -char *compute_alternate_path(const char *path, struct strbuf *err) -{ - char *ref_git = NULL; - const char *repo, *ref_git_s; - int seen_error = 0; - - ref_git_s = real_path_if_valid(path); - if (!ref_git_s) { - seen_error = 1; - strbuf_addf(err, _("path '%s' does not exist"), path); - goto out; - } else - /* - * Beware: read_gitfile(), real_path() and mkpath() - * return static buffer - */ - ref_git = xstrdup(ref_git_s); - - repo = read_gitfile(ref_git); - if (!repo) - repo = read_gitfile(mkpath("%s/.git", ref_git)); - if (repo) { - free(ref_git); - ref_git = xstrdup(repo); - } - - if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) { - char *ref_git_git = mkpathdup("%s/.git", ref_git); - free(ref_git); - ref_git = ref_git_git; - } else if (!is_directory(mkpath("%s/objects", ref_git))) { - struct strbuf sb = STRBUF_INIT; - seen_error = 1; - if (get_common_dir(&sb, ref_git)) { - strbuf_addf(err, - _("reference repository '%s' as a linked " - "checkout is not supported yet."), - path); - goto out; - } - - strbuf_addf(err, _("reference repository '%s' is not a " - "local repository."), path); - goto out; - } - - if (!access(mkpath("%s/shallow", ref_git), F_OK)) { - strbuf_addf(err, _("reference repository '%s' is shallow"), - path); - seen_error = 1; - goto out; - } - - if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) { - strbuf_addf(err, - _("reference repository '%s' is grafted"), - path); - seen_error = 1; - goto out; - } - -out: - if (seen_error) { - FREE_AND_NULL(ref_git); - } - - return ref_git; -} - -int foreach_alt_odb(alt_odb_fn fn, void *cb) -{ - struct alternate_object_database *ent; - int r = 0; - - prepare_alt_odb(the_repository); - for (ent = the_repository->objects->alt_odb_list; ent; ent = ent->next) { - r = fn(ent, cb); - if (r) - break; - } - return r; -} - -void prepare_alt_odb(struct repository *r) -{ - if (r->objects->alt_odb_tail) - return; - - r->objects->alt_odb_tail = &r->objects->alt_odb_list; - link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0); - - read_info_alternates(r, r->objects->objectdir, 0); -} - -/* Returns 1 if we have successfully freshened the file, 0 otherwise. */ -static int freshen_file(const char *fn) -{ - struct utimbuf t; - t.actime = t.modtime = time(NULL); - return !utime(fn, &t); -} - -/* - * All of the check_and_freshen functions return 1 if the file exists and was - * freshened (if freshening was requested), 0 otherwise. If they return - * 0, you should not assume that it is safe to skip a write of the object (it - * either does not exist on disk, or has a stale mtime and may be subject to - * pruning). - */ -int check_and_freshen_file(const char *fn, int freshen) -{ - if (access(fn, F_OK)) - return 0; - if (freshen && !freshen_file(fn)) - return 0; - return 1; -} - -static int check_and_freshen_local(const unsigned char *sha1, int freshen) -{ - static struct strbuf buf = STRBUF_INIT; - - strbuf_reset(&buf); - sha1_file_name(the_repository, &buf, sha1); - - return check_and_freshen_file(buf.buf, freshen); -} - -static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen) -{ - struct alternate_object_database *alt; - prepare_alt_odb(the_repository); - for (alt = the_repository->objects->alt_odb_list; alt; alt = alt->next) { - const char *path = alt_sha1_path(alt, sha1); - if (check_and_freshen_file(path, freshen)) - return 1; - } - return 0; -} - -static int check_and_freshen(const unsigned char *sha1, int freshen) -{ - return check_and_freshen_local(sha1, freshen) || - check_and_freshen_nonlocal(sha1, freshen); -} - -int has_loose_object_nonlocal(const unsigned char *sha1) -{ - return check_and_freshen_nonlocal(sha1, 0); -} - -static int has_loose_object(const unsigned char *sha1) -{ - return check_and_freshen(sha1, 0); -} - -static void mmap_limit_check(size_t length) -{ - static size_t limit = 0; - if (!limit) { - limit = git_env_ulong("GIT_MMAP_LIMIT", 0); - if (!limit) - limit = SIZE_MAX; - } - if (length > limit) - die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX, - (uintmax_t)length, (uintmax_t)limit); -} - -void *xmmap_gently(void *start, size_t length, - int prot, int flags, int fd, off_t offset) -{ - void *ret; - - mmap_limit_check(length); - ret = mmap(start, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) { - if (!length) - return NULL; - release_pack_memory(length); - ret = mmap(start, length, prot, flags, fd, offset); - } - return ret; -} - -void *xmmap(void *start, size_t length, - int prot, int flags, int fd, off_t offset) -{ - void *ret = xmmap_gently(start, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) - die_errno("mmap failed"); - return ret; -} - -/* - * With an in-core object data in "map", rehash it to make sure the - * object name actually matches "sha1" to detect object corruption. - * With "map" == NULL, try reading the object named with "sha1" using - * the streaming interface and rehash it to do the same. - */ -int check_object_signature(const struct object_id *oid, void *map, - unsigned long size, const char *type) -{ - struct object_id real_oid; - enum object_type obj_type; - struct git_istream *st; - git_hash_ctx c; - char hdr[MAX_HEADER_LEN]; - int hdrlen; - - if (map) { - hash_object_file(map, size, type, &real_oid); - return oidcmp(oid, &real_oid) ? -1 : 0; - } - - st = open_istream(oid, &obj_type, &size, NULL); - if (!st) - return -1; - - /* Generate the header */ - hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(obj_type), size) + 1; - - /* Sha1.. */ - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, hdrlen); - for (;;) { - char buf[1024 * 16]; - ssize_t readlen = read_istream(st, buf, sizeof(buf)); - - if (readlen < 0) { - close_istream(st); - return -1; - } - if (!readlen) - break; - the_hash_algo->update_fn(&c, buf, readlen); - } - the_hash_algo->final_fn(real_oid.hash, &c); - close_istream(st); - return oidcmp(oid, &real_oid) ? -1 : 0; -} - -int git_open_cloexec(const char *name, int flags) -{ - int fd; - static int o_cloexec = O_CLOEXEC; - - fd = open(name, flags | o_cloexec); - if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) { - /* Try again w/o O_CLOEXEC: the kernel might not support it */ - o_cloexec &= ~O_CLOEXEC; - fd = open(name, flags | o_cloexec); - } - -#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) - { - static int fd_cloexec = FD_CLOEXEC; - - if (!o_cloexec && 0 <= fd && fd_cloexec) { - /* Opened w/o O_CLOEXEC? try with fcntl(2) to add it */ - int flags = fcntl(fd, F_GETFD); - if (fcntl(fd, F_SETFD, flags | fd_cloexec)) - fd_cloexec = 0; - } - } -#endif - return fd; -} - -/* - * Find "sha1" as a loose object in the local repository or in an alternate. - * Returns 0 on success, negative on failure. - * - * The "path" out-parameter will give the path of the object we found (if any). - * Note that it may point to static storage and is only valid until another - * call to sha1_file_name(), etc. - */ -static int stat_sha1_file(struct repository *r, const unsigned char *sha1, - struct stat *st, const char **path) -{ - struct alternate_object_database *alt; - static struct strbuf buf = STRBUF_INIT; - - strbuf_reset(&buf); - sha1_file_name(r, &buf, sha1); - *path = buf.buf; - - if (!lstat(*path, st)) - return 0; - - prepare_alt_odb(r); - errno = ENOENT; - for (alt = r->objects->alt_odb_list; alt; alt = alt->next) { - *path = alt_sha1_path(alt, sha1); - if (!lstat(*path, st)) - return 0; - } - - return -1; -} - -/* - * Like stat_sha1_file(), but actually open the object and return the - * descriptor. See the caveats on the "path" parameter above. - */ -static int open_sha1_file(struct repository *r, - const unsigned char *sha1, const char **path) -{ - int fd; - struct alternate_object_database *alt; - int most_interesting_errno; - static struct strbuf buf = STRBUF_INIT; - - strbuf_reset(&buf); - sha1_file_name(r, &buf, sha1); - *path = buf.buf; - - fd = git_open(*path); - if (fd >= 0) - return fd; - most_interesting_errno = errno; - - prepare_alt_odb(r); - for (alt = r->objects->alt_odb_list; alt; alt = alt->next) { - *path = alt_sha1_path(alt, sha1); - fd = git_open(*path); - if (fd >= 0) - return fd; - if (most_interesting_errno == ENOENT) - most_interesting_errno = errno; - } - errno = most_interesting_errno; - return -1; -} - -/* - * Map the loose object at "path" if it is not NULL, or the path found by - * searching for a loose object named "sha1". - */ -static void *map_sha1_file_1(struct repository *r, const char *path, - const unsigned char *sha1, unsigned long *size) -{ - void *map; - int fd; - - if (path) - fd = git_open(path); - else - fd = open_sha1_file(r, sha1, &path); - map = NULL; - if (fd >= 0) { - struct stat st; - - if (!fstat(fd, &st)) { - *size = xsize_t(st.st_size); - if (!*size) { - /* mmap() is forbidden on empty files */ - error("object file %s is empty", path); - return NULL; - } - map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); - } - close(fd); - } - return map; -} - -void *map_sha1_file(struct repository *r, - const unsigned char *sha1, unsigned long *size) -{ - return map_sha1_file_1(r, NULL, sha1, size); -} - -static int unpack_sha1_short_header(git_zstream *stream, - unsigned char *map, unsigned long mapsize, - void *buffer, unsigned long bufsiz) -{ - /* Get the data stream */ - memset(stream, 0, sizeof(*stream)); - stream->next_in = map; - stream->avail_in = mapsize; - stream->next_out = buffer; - stream->avail_out = bufsiz; - - git_inflate_init(stream); - return git_inflate(stream, 0); -} - -int unpack_sha1_header(git_zstream *stream, - unsigned char *map, unsigned long mapsize, - void *buffer, unsigned long bufsiz) -{ - int status = unpack_sha1_short_header(stream, map, mapsize, - buffer, bufsiz); - - if (status < Z_OK) - return status; - - /* Make sure we have the terminating NUL */ - if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) - return -1; - return 0; -} - -static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map, - unsigned long mapsize, void *buffer, - unsigned long bufsiz, struct strbuf *header) -{ - int status; - - status = unpack_sha1_short_header(stream, map, mapsize, buffer, bufsiz); - if (status < Z_OK) - return -1; - - /* - * Check if entire header is unpacked in the first iteration. - */ - if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) - return 0; - - /* - * buffer[0..bufsiz] was not large enough. Copy the partial - * result out to header, and then append the result of further - * reading the stream. - */ - strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); - stream->next_out = buffer; - stream->avail_out = bufsiz; - - do { - status = git_inflate(stream, 0); - strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); - if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) - return 0; - stream->next_out = buffer; - stream->avail_out = bufsiz; - } while (status != Z_STREAM_END); - return -1; -} - -static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1) -{ - int bytes = strlen(buffer) + 1; - unsigned char *buf = xmallocz(size); - unsigned long n; - int status = Z_OK; - - n = stream->total_out - bytes; - if (n > size) - n = size; - memcpy(buf, (char *) buffer + bytes, n); - bytes = n; - if (bytes <= size) { - /* - * The above condition must be (bytes <= size), not - * (bytes < size). In other words, even though we - * expect no more output and set avail_out to zero, - * the input zlib stream may have bytes that express - * "this concludes the stream", and we *do* want to - * eat that input. - * - * Otherwise we would not be able to test that we - * consumed all the input to reach the expected size; - * we also want to check that zlib tells us that all - * went well with status == Z_STREAM_END at the end. - */ - stream->next_out = buf + bytes; - stream->avail_out = size - bytes; - while (status == Z_OK) - status = git_inflate(stream, Z_FINISH); - } - if (status == Z_STREAM_END && !stream->avail_in) { - git_inflate_end(stream); - return buf; - } - - if (status < 0) - error("corrupt loose object '%s'", sha1_to_hex(sha1)); - else if (stream->avail_in) - error("garbage at end of loose object '%s'", - sha1_to_hex(sha1)); - free(buf); - return NULL; -} - -/* - * We used to just use "sscanf()", but that's actually way - * too permissive for what we want to check. So do an anal - * object header parse by hand. - */ -static int parse_sha1_header_extended(const char *hdr, struct object_info *oi, - unsigned int flags) -{ - const char *type_buf = hdr; - unsigned long size; - int type, type_len = 0; - - /* - * The type can be of any size but is followed by - * a space. - */ - for (;;) { - char c = *hdr++; - if (!c) - return -1; - if (c == ' ') - break; - type_len++; - } - - type = type_from_string_gently(type_buf, type_len, 1); - if (oi->type_name) - strbuf_add(oi->type_name, type_buf, type_len); - /* - * Set type to 0 if its an unknown object and - * we're obtaining the type using '--allow-unknown-type' - * option. - */ - if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0)) - type = 0; - else if (type < 0) - die("invalid object type"); - if (oi->typep) - *oi->typep = type; - - /* - * The length must follow immediately, and be in canonical - * decimal format (ie "010" is not valid). - */ - size = *hdr++ - '0'; - if (size > 9) - return -1; - if (size) { - for (;;) { - unsigned long c = *hdr - '0'; - if (c > 9) - break; - hdr++; - size = size * 10 + c; - } - } - - if (oi->sizep) - *oi->sizep = size; - - /* - * The length must be followed by a zero byte - */ - return *hdr ? -1 : type; -} - -int parse_sha1_header(const char *hdr, unsigned long *sizep) -{ - struct object_info oi = OBJECT_INFO_INIT; - - oi.sizep = sizep; - return parse_sha1_header_extended(hdr, &oi, 0); -} - -static int sha1_loose_object_info(struct repository *r, - const unsigned char *sha1, - struct object_info *oi, int flags) -{ - int status = 0; - unsigned long mapsize; - void *map; - git_zstream stream; - char hdr[MAX_HEADER_LEN]; - struct strbuf hdrbuf = STRBUF_INIT; - unsigned long size_scratch; - - if (oi->delta_base_sha1) - hashclr(oi->delta_base_sha1); - - /* - * If we don't care about type or size, then we don't - * need to look inside the object at all. Note that we - * do not optimize out the stat call, even if the - * caller doesn't care about the disk-size, since our - * return value implicitly indicates whether the - * object even exists. - */ - if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) { - const char *path; - struct stat st; - if (stat_sha1_file(r, sha1, &st, &path) < 0) - return -1; - if (oi->disk_sizep) - *oi->disk_sizep = st.st_size; - return 0; - } - - map = map_sha1_file(r, sha1, &mapsize); - if (!map) - return -1; - - if (!oi->sizep) - oi->sizep = &size_scratch; - - if (oi->disk_sizep) - *oi->disk_sizep = mapsize; - if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) { - if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0) - status = error("unable to unpack %s header with --allow-unknown-type", - sha1_to_hex(sha1)); - } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) - status = error("unable to unpack %s header", - sha1_to_hex(sha1)); - if (status < 0) - ; /* Do nothing */ - else if (hdrbuf.len) { - if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0) - status = error("unable to parse %s header with --allow-unknown-type", - sha1_to_hex(sha1)); - } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0) - status = error("unable to parse %s header", sha1_to_hex(sha1)); - - if (status >= 0 && oi->contentp) { - *oi->contentp = unpack_sha1_rest(&stream, hdr, - *oi->sizep, sha1); - if (!*oi->contentp) { - git_inflate_end(&stream); - status = -1; - } - } else - git_inflate_end(&stream); - - munmap(map, mapsize); - if (status && oi->typep) - *oi->typep = status; - if (oi->sizep == &size_scratch) - oi->sizep = NULL; - strbuf_release(&hdrbuf); - oi->whence = OI_LOOSE; - return (status < 0) ? status : 0; -} - -int fetch_if_missing = 1; - -int oid_object_info_extended(const struct object_id *oid, struct object_info *oi, unsigned flags) -{ - static struct object_info blank_oi = OBJECT_INFO_INIT; - struct pack_entry e; - int rtype; - const struct object_id *real = oid; - int already_retried = 0; - - if (flags & OBJECT_INFO_LOOKUP_REPLACE) - real = lookup_replace_object(oid); - - if (is_null_oid(real)) - return -1; - - if (!oi) - oi = &blank_oi; - - if (!(flags & OBJECT_INFO_SKIP_CACHED)) { - struct cached_object *co = find_cached_object(real->hash); - if (co) { - if (oi->typep) - *(oi->typep) = co->type; - if (oi->sizep) - *(oi->sizep) = co->size; - if (oi->disk_sizep) - *(oi->disk_sizep) = 0; - if (oi->delta_base_sha1) - hashclr(oi->delta_base_sha1); - if (oi->type_name) - strbuf_addstr(oi->type_name, type_name(co->type)); - if (oi->contentp) - *oi->contentp = xmemdupz(co->buf, co->size); - oi->whence = OI_CACHED; - return 0; - } - } - - while (1) { - if (find_pack_entry(the_repository, real->hash, &e)) - break; - - if (flags & OBJECT_INFO_IGNORE_LOOSE) - return -1; - - /* Most likely it's a loose object. */ - if (!sha1_loose_object_info(the_repository, real->hash, oi, flags)) - return 0; - - /* Not a loose object; someone else may have just packed it. */ - if (!(flags & OBJECT_INFO_QUICK)) { - reprepare_packed_git(the_repository); - if (find_pack_entry(the_repository, real->hash, &e)) - break; - } - - /* Check if it is a missing object */ - if (fetch_if_missing && repository_format_partial_clone && - !already_retried) { - /* - * TODO Investigate haveing fetch_object() return - * TODO error/success and stopping the music here. - */ - fetch_object(repository_format_partial_clone, real->hash); - already_retried = 1; - continue; - } - - return -1; - } - - if (oi == &blank_oi) - /* - * We know that the caller doesn't actually need the - * information below, so return early. - */ - return 0; - rtype = packed_object_info(e.p, e.offset, oi); - if (rtype < 0) { - mark_bad_packed_object(e.p, real->hash); - return oid_object_info_extended(real, oi, 0); - } else if (oi->whence == OI_PACKED) { - oi->u.packed.offset = e.offset; - oi->u.packed.pack = e.p; - oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA || - rtype == OBJ_OFS_DELTA); - } - - return 0; -} - -/* returns enum object_type or negative */ -int oid_object_info(const struct object_id *oid, unsigned long *sizep) -{ - enum object_type type; - struct object_info oi = OBJECT_INFO_INIT; - - oi.typep = &type; - oi.sizep = sizep; - if (oid_object_info_extended(oid, &oi, - OBJECT_INFO_LOOKUP_REPLACE) < 0) - return -1; - return type; -} - -static void *read_object(const unsigned char *sha1, enum object_type *type, - unsigned long *size) -{ - struct object_id oid; - struct object_info oi = OBJECT_INFO_INIT; - void *content; - oi.typep = type; - oi.sizep = size; - oi.contentp = &content; - - hashcpy(oid.hash, sha1); - - if (oid_object_info_extended(&oid, &oi, 0) < 0) - return NULL; - return content; -} - -int pretend_object_file(void *buf, unsigned long len, enum object_type type, - struct object_id *oid) -{ - struct cached_object *co; - - hash_object_file(buf, len, type_name(type), oid); - if (has_sha1_file(oid->hash) || find_cached_object(oid->hash)) - return 0; - ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc); - co = &cached_objects[cached_object_nr++]; - co->size = len; - co->type = type; - co->buf = xmalloc(len); - memcpy(co->buf, buf, len); - hashcpy(co->sha1, oid->hash); - return 0; -} - -/* - * This function dies on corrupt objects; the callers who want to - * deal with them should arrange to call read_object() and give error - * messages themselves. - */ -void *read_object_file_extended(const struct object_id *oid, - enum object_type *type, - unsigned long *size, - int lookup_replace) -{ - void *data; - const struct packed_git *p; - const char *path; - struct stat st; - const struct object_id *repl = lookup_replace ? lookup_replace_object(oid) - : oid; - - errno = 0; - data = read_object(repl->hash, type, size); - if (data) - return data; - - if (errno && errno != ENOENT) - die_errno("failed to read object %s", oid_to_hex(oid)); - - /* die if we replaced an object with one that does not exist */ - if (repl != oid) - die("replacement %s not found for %s", - oid_to_hex(repl), oid_to_hex(oid)); - - if (!stat_sha1_file(the_repository, repl->hash, &st, &path)) - die("loose object %s (stored in %s) is corrupt", - oid_to_hex(repl), path); - - if ((p = has_packed_and_bad(repl->hash)) != NULL) - die("packed object %s (stored in %s) is corrupt", - oid_to_hex(repl), p->pack_name); - - return NULL; -} - -void *read_object_with_reference(const struct object_id *oid, - const char *required_type_name, - unsigned long *size, - struct object_id *actual_oid_return) -{ - enum object_type type, required_type; - void *buffer; - unsigned long isize; - struct object_id actual_oid; - - required_type = type_from_string(required_type_name); - oidcpy(&actual_oid, oid); - while (1) { - int ref_length = -1; - const char *ref_type = NULL; - - buffer = read_object_file(&actual_oid, &type, &isize); - if (!buffer) - return NULL; - if (type == required_type) { - *size = isize; - if (actual_oid_return) - oidcpy(actual_oid_return, &actual_oid); - return buffer; - } - /* Handle references */ - else if (type == OBJ_COMMIT) - ref_type = "tree "; - else if (type == OBJ_TAG) - ref_type = "object "; - else { - free(buffer); - return NULL; - } - ref_length = strlen(ref_type); - - if (ref_length + GIT_SHA1_HEXSZ > isize || - memcmp(buffer, ref_type, ref_length) || - get_oid_hex((char *) buffer + ref_length, &actual_oid)) { - free(buffer); - return NULL; - } - free(buffer); - /* Now we have the ID of the referred-to object in - * actual_oid. Check again. */ - } -} - -static void write_object_file_prepare(const void *buf, unsigned long len, - const char *type, struct object_id *oid, - char *hdr, int *hdrlen) -{ - git_hash_ctx c; - - /* Generate the header */ - *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1; - - /* Sha1.. */ - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, *hdrlen); - the_hash_algo->update_fn(&c, buf, len); - the_hash_algo->final_fn(oid->hash, &c); -} - -/* - * Move the just written object into its final resting place. - */ -int finalize_object_file(const char *tmpfile, const char *filename) -{ - int ret = 0; - - if (object_creation_mode == OBJECT_CREATION_USES_RENAMES) - goto try_rename; - else if (link(tmpfile, filename)) - ret = errno; - - /* - * Coda hack - coda doesn't like cross-directory links, - * so we fall back to a rename, which will mean that it - * won't be able to check collisions, but that's not a - * big deal. - * - * The same holds for FAT formatted media. - * - * When this succeeds, we just return. We have nothing - * left to unlink. - */ - if (ret && ret != EEXIST) { - try_rename: - if (!rename(tmpfile, filename)) - goto out; - ret = errno; - } - unlink_or_warn(tmpfile); - if (ret) { - if (ret != EEXIST) { - return error_errno("unable to write sha1 filename %s", filename); - } - /* FIXME!!! Collision check here ? */ - } - -out: - if (adjust_shared_perm(filename)) - return error("unable to set permission to '%s'", filename); - return 0; -} - -static int write_buffer(int fd, const void *buf, size_t len) -{ - if (write_in_full(fd, buf, len) < 0) - return error_errno("file write error"); - return 0; -} - -int hash_object_file(const void *buf, unsigned long len, const char *type, - struct object_id *oid) -{ - char hdr[MAX_HEADER_LEN]; - int hdrlen = sizeof(hdr); - write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen); - return 0; -} - -/* Finalize a file on disk, and close it. */ -static void close_sha1_file(int fd) -{ - if (fsync_object_files) - fsync_or_die(fd, "sha1 file"); - if (close(fd) != 0) - die_errno("error when closing sha1 file"); -} - -/* Size of directory component, including the ending '/' */ -static inline int directory_size(const char *filename) -{ - const char *s = strrchr(filename, '/'); - if (!s) - return 0; - return s - filename + 1; -} - -/* - * This creates a temporary file in the same directory as the final - * 'filename' - * - * We want to avoid cross-directory filename renames, because those - * can have problems on various filesystems (FAT, NFS, Coda). - */ -static int create_tmpfile(struct strbuf *tmp, const char *filename) -{ - int fd, dirlen = directory_size(filename); - - strbuf_reset(tmp); - strbuf_add(tmp, filename, dirlen); - strbuf_addstr(tmp, "tmp_obj_XXXXXX"); - fd = git_mkstemp_mode(tmp->buf, 0444); - if (fd < 0 && dirlen && errno == ENOENT) { - /* - * Make sure the directory exists; note that the contents - * of the buffer are undefined after mkstemp returns an - * error, so we have to rewrite the whole buffer from - * scratch. - */ - strbuf_reset(tmp); - strbuf_add(tmp, filename, dirlen - 1); - if (mkdir(tmp->buf, 0777) && errno != EEXIST) - return -1; - if (adjust_shared_perm(tmp->buf)) - return -1; - - /* Try again */ - strbuf_addstr(tmp, "/tmp_obj_XXXXXX"); - fd = git_mkstemp_mode(tmp->buf, 0444); - } - return fd; -} - -static int write_loose_object(const struct object_id *oid, char *hdr, - int hdrlen, const void *buf, unsigned long len, - time_t mtime) -{ - int fd, ret; - unsigned char compressed[4096]; - git_zstream stream; - git_hash_ctx c; - struct object_id parano_oid; - static struct strbuf tmp_file = STRBUF_INIT; - static struct strbuf filename = STRBUF_INIT; - - strbuf_reset(&filename); - sha1_file_name(the_repository, &filename, oid->hash); - - fd = create_tmpfile(&tmp_file, filename.buf); - if (fd < 0) { - if (errno == EACCES) - return error("insufficient permission for adding an object to repository database %s", get_object_directory()); - else - return error_errno("unable to create temporary file"); - } - - /* Set it up */ - git_deflate_init(&stream, zlib_compression_level); - stream.next_out = compressed; - stream.avail_out = sizeof(compressed); - the_hash_algo->init_fn(&c); - - /* First header.. */ - stream.next_in = (unsigned char *)hdr; - stream.avail_in = hdrlen; - while (git_deflate(&stream, 0) == Z_OK) - ; /* nothing */ - the_hash_algo->update_fn(&c, hdr, hdrlen); - - /* Then the data itself.. */ - stream.next_in = (void *)buf; - stream.avail_in = len; - do { - unsigned char *in0 = stream.next_in; - ret = git_deflate(&stream, Z_FINISH); - the_hash_algo->update_fn(&c, in0, stream.next_in - in0); - if (write_buffer(fd, compressed, stream.next_out - compressed) < 0) - die("unable to write sha1 file"); - stream.next_out = compressed; - stream.avail_out = sizeof(compressed); - } while (ret == Z_OK); - - if (ret != Z_STREAM_END) - die("unable to deflate new object %s (%d)", oid_to_hex(oid), - ret); - ret = git_deflate_end_gently(&stream); - if (ret != Z_OK) - die("deflateEnd on object %s failed (%d)", oid_to_hex(oid), - ret); - the_hash_algo->final_fn(parano_oid.hash, &c); - if (oidcmp(oid, ¶no_oid) != 0) - die("confused by unstable object source data for %s", - oid_to_hex(oid)); - - close_sha1_file(fd); - - if (mtime) { - struct utimbuf utb; - utb.actime = mtime; - utb.modtime = mtime; - if (utime(tmp_file.buf, &utb) < 0) - warning_errno("failed utime() on %s", tmp_file.buf); - } - - return finalize_object_file(tmp_file.buf, filename.buf); -} - -static int freshen_loose_object(const unsigned char *sha1) -{ - return check_and_freshen(sha1, 1); -} - -static int freshen_packed_object(const unsigned char *sha1) -{ - struct pack_entry e; - if (!find_pack_entry(the_repository, sha1, &e)) - return 0; - if (e.p->freshened) - return 1; - if (!freshen_file(e.p->pack_name)) - return 0; - e.p->freshened = 1; - return 1; -} - -int write_object_file(const void *buf, unsigned long len, const char *type, - struct object_id *oid) -{ - char hdr[MAX_HEADER_LEN]; - int hdrlen = sizeof(hdr); - - /* Normally if we have it in the pack then we do not bother writing - * it out into .git/objects/??/?{38} file. - */ - write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen); - if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) - return 0; - return write_loose_object(oid, hdr, hdrlen, buf, len, 0); -} - -int hash_object_file_literally(const void *buf, unsigned long len, - const char *type, struct object_id *oid, - unsigned flags) -{ - char *header; - int hdrlen, status = 0; - - /* type string, SP, %lu of the length plus NUL must fit this */ - hdrlen = strlen(type) + MAX_HEADER_LEN; - header = xmalloc(hdrlen); - write_object_file_prepare(buf, len, type, oid, header, &hdrlen); - - if (!(flags & HASH_WRITE_OBJECT)) - goto cleanup; - if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) - goto cleanup; - status = write_loose_object(oid, header, hdrlen, buf, len, 0); - -cleanup: - free(header); - return status; -} - -int force_object_loose(const struct object_id *oid, time_t mtime) -{ - void *buf; - unsigned long len; - enum object_type type; - char hdr[MAX_HEADER_LEN]; - int hdrlen; - int ret; - - if (has_loose_object(oid->hash)) - return 0; - buf = read_object(oid->hash, &type, &len); - if (!buf) - return error("cannot read sha1_file for %s", oid_to_hex(oid)); - hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), len) + 1; - ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime); - free(buf); - - return ret; -} - -int has_sha1_file_with_flags(const unsigned char *sha1, int flags) -{ - struct object_id oid; - if (!startup_info->have_repository) - return 0; - hashcpy(oid.hash, sha1); - return oid_object_info_extended(&oid, NULL, - flags | OBJECT_INFO_SKIP_CACHED) >= 0; -} - -int has_object_file(const struct object_id *oid) -{ - return has_sha1_file(oid->hash); -} - -int has_object_file_with_flags(const struct object_id *oid, int flags) -{ - return has_sha1_file_with_flags(oid->hash, flags); -} - -static void check_tree(const void *buf, size_t size) -{ - struct tree_desc desc; - struct name_entry entry; - - init_tree_desc(&desc, buf, size); - while (tree_entry(&desc, &entry)) - /* do nothing - * tree_entry() will die() on malformed entries */ - ; -} - -static void check_commit(const void *buf, size_t size) -{ - struct commit c; - memset(&c, 0, sizeof(c)); - if (parse_commit_buffer(&c, buf, size)) - die("corrupt commit"); -} - -static void check_tag(const void *buf, size_t size) -{ - struct tag t; - memset(&t, 0, sizeof(t)); - if (parse_tag_buffer(&t, buf, size)) - die("corrupt tag"); -} - -static int index_mem(struct object_id *oid, void *buf, size_t size, - enum object_type type, - const char *path, unsigned flags) -{ - int ret, re_allocated = 0; - int write_object = flags & HASH_WRITE_OBJECT; - - if (!type) - type = OBJ_BLOB; - - /* - * Convert blobs to git internal format - */ - if ((type == OBJ_BLOB) && path) { - struct strbuf nbuf = STRBUF_INIT; - if (convert_to_git(&the_index, path, buf, size, &nbuf, - get_conv_flags(flags))) { - buf = strbuf_detach(&nbuf, &size); - re_allocated = 1; - } - } - if (flags & HASH_FORMAT_CHECK) { - if (type == OBJ_TREE) - check_tree(buf, size); - if (type == OBJ_COMMIT) - check_commit(buf, size); - if (type == OBJ_TAG) - check_tag(buf, size); - } - - if (write_object) - ret = write_object_file(buf, size, type_name(type), oid); - else - ret = hash_object_file(buf, size, type_name(type), oid); - if (re_allocated) - free(buf); - return ret; -} - -static int index_stream_convert_blob(struct object_id *oid, int fd, - const char *path, unsigned flags) -{ - int ret; - const int write_object = flags & HASH_WRITE_OBJECT; - struct strbuf sbuf = STRBUF_INIT; - - assert(path); - assert(would_convert_to_git_filter_fd(path)); - - convert_to_git_filter_fd(&the_index, path, fd, &sbuf, - get_conv_flags(flags)); - - if (write_object) - ret = write_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB), - oid); - else - ret = hash_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB), - oid); - strbuf_release(&sbuf); - return ret; -} - -static int index_pipe(struct object_id *oid, int fd, enum object_type type, - const char *path, unsigned flags) -{ - struct strbuf sbuf = STRBUF_INIT; - int ret; - - if (strbuf_read(&sbuf, fd, 4096) >= 0) - ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags); - else - ret = -1; - strbuf_release(&sbuf); - return ret; -} - -#define SMALL_FILE_SIZE (32*1024) - -static int index_core(struct object_id *oid, int fd, size_t size, - enum object_type type, const char *path, - unsigned flags) -{ - int ret; - - if (!size) { - ret = index_mem(oid, "", size, type, path, flags); - } else if (size <= SMALL_FILE_SIZE) { - char *buf = xmalloc(size); - ssize_t read_result = read_in_full(fd, buf, size); - if (read_result < 0) - ret = error_errno("read error while indexing %s", - path ? path : ""); - else if (read_result != size) - ret = error("short read while indexing %s", - path ? path : ""); - else - ret = index_mem(oid, buf, size, type, path, flags); - free(buf); - } else { - void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - ret = index_mem(oid, buf, size, type, path, flags); - munmap(buf, size); - } - return ret; -} - -/* - * This creates one packfile per large blob unless bulk-checkin - * machinery is "plugged". - * - * This also bypasses the usual "convert-to-git" dance, and that is on - * purpose. We could write a streaming version of the converting - * functions and insert that before feeding the data to fast-import - * (or equivalent in-core API described above). However, that is - * somewhat complicated, as we do not know the size of the filter - * result, which we need to know beforehand when writing a git object. - * Since the primary motivation for trying to stream from the working - * tree file and to avoid mmaping it in core is to deal with large - * binary blobs, they generally do not want to get any conversion, and - * callers should avoid this code path when filters are requested. - */ -static int index_stream(struct object_id *oid, int fd, size_t size, - enum object_type type, const char *path, - unsigned flags) -{ - return index_bulk_checkin(oid, fd, size, type, path, flags); -} - -int index_fd(struct object_id *oid, int fd, struct stat *st, - enum object_type type, const char *path, unsigned flags) -{ - int ret; - - /* - * Call xsize_t() only when needed to avoid potentially unnecessary - * die() for large files. - */ - if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path)) - ret = index_stream_convert_blob(oid, fd, path, flags); - else if (!S_ISREG(st->st_mode)) - ret = index_pipe(oid, fd, type, path, flags); - else if (st->st_size <= big_file_threshold || type != OBJ_BLOB || - (path && would_convert_to_git(&the_index, path))) - ret = index_core(oid, fd, xsize_t(st->st_size), type, path, - flags); - else - ret = index_stream(oid, fd, xsize_t(st->st_size), type, path, - flags); - close(fd); - return ret; -} - -int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags) -{ - int fd; - struct strbuf sb = STRBUF_INIT; - int rc = 0; - - switch (st->st_mode & S_IFMT) { - case S_IFREG: - fd = open(path, O_RDONLY); - if (fd < 0) - return error_errno("open(\"%s\")", path); - if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0) - return error("%s: failed to insert into database", - path); - break; - case S_IFLNK: - if (strbuf_readlink(&sb, path, st->st_size)) - return error_errno("readlink(\"%s\")", path); - if (!(flags & HASH_WRITE_OBJECT)) - hash_object_file(sb.buf, sb.len, blob_type, oid); - else if (write_object_file(sb.buf, sb.len, blob_type, oid)) - rc = error("%s: failed to insert into database", path); - strbuf_release(&sb); - break; - case S_IFDIR: - return resolve_gitlink_ref(path, "HEAD", oid); - default: - return error("%s: unsupported file type", path); - } - return rc; -} - -int read_pack_header(int fd, struct pack_header *header) -{ - if (read_in_full(fd, header, sizeof(*header)) != sizeof(*header)) - /* "eof before pack header was fully read" */ - return PH_ERROR_EOF; - - if (header->hdr_signature != htonl(PACK_SIGNATURE)) - /* "protocol error (pack signature mismatch detected)" */ - return PH_ERROR_PACK_SIGNATURE; - if (!pack_version_ok(header->hdr_version)) - /* "protocol error (pack version unsupported)" */ - return PH_ERROR_PROTOCOL; - return 0; -} - -void assert_oid_type(const struct object_id *oid, enum object_type expect) -{ - enum object_type type = oid_object_info(oid, NULL); - if (type < 0) - die("%s is not a valid object", oid_to_hex(oid)); - if (type != expect) - die("%s is not a valid '%s' object", oid_to_hex(oid), - type_name(expect)); -} - -int for_each_file_in_obj_subdir(unsigned int subdir_nr, - struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) -{ - size_t origlen, baselen; - DIR *dir; - struct dirent *de; - int r = 0; - struct object_id oid; - - if (subdir_nr > 0xff) - BUG("invalid loose object subdirectory: %x", subdir_nr); - - origlen = path->len; - strbuf_complete(path, '/'); - strbuf_addf(path, "%02x", subdir_nr); - - dir = opendir(path->buf); - if (!dir) { - if (errno != ENOENT) - r = error_errno("unable to open %s", path->buf); - strbuf_setlen(path, origlen); - return r; - } - - oid.hash[0] = subdir_nr; - strbuf_addch(path, '/'); - baselen = path->len; - - while ((de = readdir(dir))) { - size_t namelen; - if (is_dot_or_dotdot(de->d_name)) - continue; - - namelen = strlen(de->d_name); - strbuf_setlen(path, baselen); - strbuf_add(path, de->d_name, namelen); - if (namelen == GIT_SHA1_HEXSZ - 2 && - !hex_to_bytes(oid.hash + 1, de->d_name, - GIT_SHA1_RAWSZ - 1)) { - if (obj_cb) { - r = obj_cb(&oid, path->buf, data); - if (r) - break; - } - continue; - } - - if (cruft_cb) { - r = cruft_cb(de->d_name, path->buf, data); - if (r) - break; - } - } - closedir(dir); - - strbuf_setlen(path, baselen - 1); - if (!r && subdir_cb) - r = subdir_cb(subdir_nr, path->buf, data); - - strbuf_setlen(path, origlen); - - return r; -} - -int for_each_loose_file_in_objdir_buf(struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) -{ - int r = 0; - int i; - - for (i = 0; i < 256; i++) { - r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb, - subdir_cb, data); - if (r) - break; - } - - return r; -} - -int for_each_loose_file_in_objdir(const char *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) -{ - struct strbuf buf = STRBUF_INIT; - int r; - - strbuf_addstr(&buf, path); - r = for_each_loose_file_in_objdir_buf(&buf, obj_cb, cruft_cb, - subdir_cb, data); - strbuf_release(&buf); - - return r; -} - -struct loose_alt_odb_data { - each_loose_object_fn *cb; - void *data; -}; - -static int loose_from_alt_odb(struct alternate_object_database *alt, - void *vdata) -{ - struct loose_alt_odb_data *data = vdata; - struct strbuf buf = STRBUF_INIT; - int r; - - strbuf_addstr(&buf, alt->path); - r = for_each_loose_file_in_objdir_buf(&buf, - data->cb, NULL, NULL, - data->data); - strbuf_release(&buf); - return r; -} - -int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags) -{ - struct loose_alt_odb_data alt; - int r; - - r = for_each_loose_file_in_objdir(get_object_directory(), - cb, NULL, NULL, data); - if (r) - return r; - - if (flags & FOR_EACH_OBJECT_LOCAL_ONLY) - return 0; - - alt.cb = cb; - alt.data = data; - return foreach_alt_odb(loose_from_alt_odb, &alt); -} - -static int check_stream_sha1(git_zstream *stream, - const char *hdr, - unsigned long size, - const char *path, - const unsigned char *expected_sha1) -{ - git_hash_ctx c; - unsigned char real_sha1[GIT_MAX_RAWSZ]; - unsigned char buf[4096]; - unsigned long total_read; - int status = Z_OK; - - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, stream->total_out); - - /* - * We already read some bytes into hdr, but the ones up to the NUL - * do not count against the object's content size. - */ - total_read = stream->total_out - strlen(hdr) - 1; - - /* - * This size comparison must be "<=" to read the final zlib packets; - * see the comment in unpack_sha1_rest for details. - */ - while (total_read <= size && - (status == Z_OK || status == Z_BUF_ERROR)) { - stream->next_out = buf; - stream->avail_out = sizeof(buf); - if (size - total_read < stream->avail_out) - stream->avail_out = size - total_read; - status = git_inflate(stream, Z_FINISH); - the_hash_algo->update_fn(&c, buf, stream->next_out - buf); - total_read += stream->next_out - buf; - } - git_inflate_end(stream); - - if (status != Z_STREAM_END) { - error("corrupt loose object '%s'", sha1_to_hex(expected_sha1)); - return -1; - } - if (stream->avail_in) { - error("garbage at end of loose object '%s'", - sha1_to_hex(expected_sha1)); - return -1; - } - - the_hash_algo->final_fn(real_sha1, &c); - if (hashcmp(expected_sha1, real_sha1)) { - error("sha1 mismatch for %s (expected %s)", path, - sha1_to_hex(expected_sha1)); - return -1; - } - - return 0; -} - -int read_loose_object(const char *path, - const struct object_id *expected_oid, - enum object_type *type, - unsigned long *size, - void **contents) -{ - int ret = -1; - void *map = NULL; - unsigned long mapsize; - git_zstream stream; - char hdr[MAX_HEADER_LEN]; - - *contents = NULL; - - map = map_sha1_file_1(the_repository, path, NULL, &mapsize); - if (!map) { - error_errno("unable to mmap %s", path); - goto out; - } - - if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) { - error("unable to unpack header of %s", path); - goto out; - } - - *type = parse_sha1_header(hdr, size); - if (*type < 0) { - error("unable to parse header of %s", path); - git_inflate_end(&stream); - goto out; - } - - if (*type == OBJ_BLOB) { - if (check_stream_sha1(&stream, hdr, *size, path, expected_oid->hash) < 0) - goto out; - } else { - *contents = unpack_sha1_rest(&stream, hdr, *size, expected_oid->hash); - if (!*contents) { - error("unable to unpack contents of %s", path); - git_inflate_end(&stream); - goto out; - } - if (check_object_signature(expected_oid, *contents, - *size, type_name(*type))) { - error("sha1 mismatch for %s (expected %s)", path, - oid_to_hex(expected_oid)); - free(*contents); - goto out; - } - } - - ret = 0; /* everything checks out */ - -out: - if (map) - munmap(map, mapsize); - return ret; -} -- cgit v1.2.1 From 11bc058ce66a437499a774e82398c96e1324e2e0 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 10 Apr 2018 14:26:21 -0700 Subject: replace_object.c: rename to use dash in file name This is more consistent with the project style. The majority of Git's source files use dashes in preference to underscores in their file names. Noticed while adding a header corresponding to this file. Signed-off-by: Jonathan Nieder Signed-off-by: Stefan Beller --- Makefile | 2 +- replace-object.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ replace_object.c | 120 ------------------------------------------------------- 3 files changed, 121 insertions(+), 121 deletions(-) create mode 100644 replace-object.c delete mode 100644 replace_object.c diff --git a/Makefile b/Makefile index ce26efa7dc..7262b7c942 100644 --- a/Makefile +++ b/Makefile @@ -888,7 +888,7 @@ LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o -LIB_OBJS += replace_object.o +LIB_OBJS += replace-object.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o diff --git a/replace-object.c b/replace-object.c new file mode 100644 index 0000000000..336357394d --- /dev/null +++ b/replace-object.c @@ -0,0 +1,120 @@ +#include "cache.h" +#include "sha1-lookup.h" +#include "refs.h" +#include "commit.h" + +/* + * An array of replacements. The array is kept sorted by the original + * sha1. + */ +static struct replace_object { + struct object_id original; + struct object_id replacement; +} **replace_object; + +static int replace_object_alloc, replace_object_nr; + +static const unsigned char *replace_sha1_access(size_t index, void *table) +{ + struct replace_object **replace = table; + return replace[index]->original.hash; +} + +static int replace_object_pos(const unsigned char *sha1) +{ + return sha1_pos(sha1, replace_object, replace_object_nr, + replace_sha1_access); +} + +static int register_replace_object(struct replace_object *replace, + int ignore_dups) +{ + int pos = replace_object_pos(replace->original.hash); + + if (0 <= pos) { + if (ignore_dups) + free(replace); + else { + free(replace_object[pos]); + replace_object[pos] = replace; + } + return 1; + } + pos = -pos - 1; + ALLOC_GROW(replace_object, replace_object_nr + 1, replace_object_alloc); + replace_object_nr++; + if (pos < replace_object_nr) + MOVE_ARRAY(replace_object + pos + 1, replace_object + pos, + replace_object_nr - pos - 1); + replace_object[pos] = replace; + return 0; +} + +static int register_replace_ref(const char *refname, + const struct object_id *oid, + int flag, void *cb_data) +{ + /* Get sha1 from refname */ + const char *slash = strrchr(refname, '/'); + const char *hash = slash ? slash + 1 : refname; + struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj)); + + if (get_oid_hex(hash, &repl_obj->original)) { + free(repl_obj); + warning("bad replace ref name: %s", refname); + return 0; + } + + /* Copy sha1 from the read ref */ + oidcpy(&repl_obj->replacement, oid); + + /* Register new object */ + if (register_replace_object(repl_obj, 1)) + die("duplicate replace ref: %s", refname); + + return 0; +} + +static void prepare_replace_object(void) +{ + static int replace_object_prepared; + + if (replace_object_prepared) + return; + + for_each_replace_ref(register_replace_ref, NULL); + replace_object_prepared = 1; + if (!replace_object_nr) + check_replace_refs = 0; +} + +/* We allow "recursive" replacement. Only within reason, though */ +#define MAXREPLACEDEPTH 5 + +/* + * If a replacement for object oid has been set up, return the + * replacement object's name (replaced recursively, if necessary). + * The return value is either oid or a pointer to a + * permanently-allocated value. This function always respects replace + * references, regardless of the value of check_replace_refs. + */ +const struct object_id *do_lookup_replace_object(const struct object_id *oid) +{ + int pos, depth = MAXREPLACEDEPTH; + const struct object_id *cur = oid; + + prepare_replace_object(); + + /* Try to recursively replace the object */ + do { + if (--depth < 0) + die("replace depth too high for object %s", + oid_to_hex(oid)); + + pos = replace_object_pos(cur->hash); + if (0 <= pos) + cur = &replace_object[pos]->replacement; + } while (0 <= pos); + + return cur; +} diff --git a/replace_object.c b/replace_object.c deleted file mode 100644 index 336357394d..0000000000 --- a/replace_object.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "cache.h" -#include "sha1-lookup.h" -#include "refs.h" -#include "commit.h" - -/* - * An array of replacements. The array is kept sorted by the original - * sha1. - */ -static struct replace_object { - struct object_id original; - struct object_id replacement; -} **replace_object; - -static int replace_object_alloc, replace_object_nr; - -static const unsigned char *replace_sha1_access(size_t index, void *table) -{ - struct replace_object **replace = table; - return replace[index]->original.hash; -} - -static int replace_object_pos(const unsigned char *sha1) -{ - return sha1_pos(sha1, replace_object, replace_object_nr, - replace_sha1_access); -} - -static int register_replace_object(struct replace_object *replace, - int ignore_dups) -{ - int pos = replace_object_pos(replace->original.hash); - - if (0 <= pos) { - if (ignore_dups) - free(replace); - else { - free(replace_object[pos]); - replace_object[pos] = replace; - } - return 1; - } - pos = -pos - 1; - ALLOC_GROW(replace_object, replace_object_nr + 1, replace_object_alloc); - replace_object_nr++; - if (pos < replace_object_nr) - MOVE_ARRAY(replace_object + pos + 1, replace_object + pos, - replace_object_nr - pos - 1); - replace_object[pos] = replace; - return 0; -} - -static int register_replace_ref(const char *refname, - const struct object_id *oid, - int flag, void *cb_data) -{ - /* Get sha1 from refname */ - const char *slash = strrchr(refname, '/'); - const char *hash = slash ? slash + 1 : refname; - struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj)); - - if (get_oid_hex(hash, &repl_obj->original)) { - free(repl_obj); - warning("bad replace ref name: %s", refname); - return 0; - } - - /* Copy sha1 from the read ref */ - oidcpy(&repl_obj->replacement, oid); - - /* Register new object */ - if (register_replace_object(repl_obj, 1)) - die("duplicate replace ref: %s", refname); - - return 0; -} - -static void prepare_replace_object(void) -{ - static int replace_object_prepared; - - if (replace_object_prepared) - return; - - for_each_replace_ref(register_replace_ref, NULL); - replace_object_prepared = 1; - if (!replace_object_nr) - check_replace_refs = 0; -} - -/* We allow "recursive" replacement. Only within reason, though */ -#define MAXREPLACEDEPTH 5 - -/* - * If a replacement for object oid has been set up, return the - * replacement object's name (replaced recursively, if necessary). - * The return value is either oid or a pointer to a - * permanently-allocated value. This function always respects replace - * references, regardless of the value of check_replace_refs. - */ -const struct object_id *do_lookup_replace_object(const struct object_id *oid) -{ - int pos, depth = MAXREPLACEDEPTH; - const struct object_id *cur = oid; - - prepare_replace_object(); - - /* Try to recursively replace the object */ - do { - if (--depth < 0) - die("replace depth too high for object %s", - oid_to_hex(oid)); - - pos = replace_object_pos(cur->hash); - if (0 <= pos) - cur = &replace_object[pos]->replacement; - } while (0 <= pos); - - return cur; -} -- cgit v1.2.1