From ec1fcc16af94810dd822c6da533f58fa2750f14a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 7 Oct 2005 03:42:00 -0700 Subject: Show original and resulting blob object info in diff output. This adds more cruft to diff --git header to record the blob SHA1 and the mode the patch/diff is intended to be applied against, to help the receiving end fall back on a three-way merge. The new header looks like this: diff --git a/apply.c b/apply.c index 7be5041..8366082 100644 --- a/apply.c +++ b/apply.c @@ -14,6 +14,7 @@ // files that are being modified, but doesn't apply the patch // --stat does just a diffstat, and doesn't actually apply +// --show-index-info shows the old and new index info for... ... Upon receiving such a patch, if the patch did not apply cleanly to the target tree, the recipient can try to find the matching old objects in her object database and create a temporary tree, apply the patch to that temporary tree, and attempt a 3-way merge between the patched temporary tree and the target tree using the original temporary tree as the common ancestor. The patch lifts the code to compute the hash for an on-filesystem object from update-index.c and makes it available to the diff output routine. Signed-off-by: Junio C Hamano --- t/diff-lib.sh | 10 ++++++++-- t/t4000-diff-format.sh | 3 ++- t/t4001-diff-rename.sh | 3 ++- t/t4004-diff-rename-symlink.sh | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) (limited to 't') diff --git a/t/diff-lib.sh b/t/diff-lib.sh index a912f435aa..745a1b0311 100755 --- a/t/diff-lib.sh +++ b/t/diff-lib.sh @@ -29,7 +29,13 @@ compare_diff_raw_z () { compare_diff_patch () { # When heuristics are improved, the score numbers would change. # Ignore them while comparing. - sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1 - sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2 + sed -e ' + /^[dis]*imilarity index [0-9]*%$/d + /^index [0-9a-f]*\.\.[0-9a-f]/d + ' <"$1" >.tmp-1 + sed -e ' + /^[dis]*imilarity index [0-9]*%$/d + /^index [0-9a-f]*\.\.[0-9a-f]/d + ' <"$2" >.tmp-2 diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index f3b6330a9b..beb6d8f487 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -7,6 +7,7 @@ test_description='Test built-in diff output engine. ' . ./test-lib.sh +. ../diff-lib.sh echo >path0 'Line 1 Line 2 @@ -48,6 +49,6 @@ EOF test_expect_success \ 'validate git-diff-files -p output.' \ - 'cmp -s current expected' + 'compare_diff_patch current expected' test_done diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index be47485682..2e3c20d6b9 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine. ' . ./test-lib.sh +. ../diff-lib.sh echo >path0 'Line 1 Line 2 @@ -61,6 +62,6 @@ EOF test_expect_success \ 'validate the output.' \ - 'diff -I "similarity.*" >/dev/null current expected' + 'compare_diff_patch current expected' test_done diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh index f59614ae25..a23aaa0a94 100755 --- a/t/t4004-diff-rename-symlink.sh +++ b/t/t4004-diff-rename-symlink.sh @@ -10,6 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed by an edit for them. ' . ./test-lib.sh +. ../diff-lib.sh test_expect_success \ 'prepare reference tree' \ @@ -61,6 +62,6 @@ EOF test_expect_success \ 'validate diff output' \ - 'diff -u current expected' + 'compare_diff_patch current expected' test_done -- cgit v1.3-5-g45d5 From 230f13225df8b7e7eb0acc91a8c630f9e84967c1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 8 Oct 2005 15:54:01 -0700 Subject: Create object subdirectories on demand This makes it possible to have a "sparse" git object subdirectory structure, something that has become much more attractive now that people use pack-files all the time. As a result of pack-files, a git object directory doesn't necessarily have any individual objects lying around, and in that case it's just wasting space to keep the empty first-level object directories around: on many filesystems the 256 empty directories will be aboue 1MB of diskspace. Even more importantly, after you re-pack a project that _used_ to be unpacked, you could be left with huge directories that no longer contain anything, but that waste space and take time to look through. With this change, "git prune-packed" can just do an rmdir() on the directories, and they'll get removed if empty, and re-created on demand. This patch also tries to fix up "write_sha1_from_fd()" to use the new common infrastructure for creating the object files, closing a hole where we might otherwise leave half-written objects in the object database. [jc: I unoptimized the part that really removes the fan-out directories to ease transition. init-db still wastes 1MB of diskspace to hold 256 empty fan-outs, and prune-packed rmdir()'s the grown but empty directories, but runs mkdir() immediately after that -- reducing the saving from 150KB to 146KB. These parts will be re-introduced when everybody has the on-demand capability.] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- daemon.c | 2 +- fsck-objects.c | 5 +-- git-rename.perl | 2 +- git-sh-setup.sh | 2 +- prune-packed.c | 5 ++- sha1_file.c | 114 +++++++++++++++++++++++++++++++++++++------------------ t/t0000-basic.sh | 2 +- 7 files changed, 88 insertions(+), 44 deletions(-) (limited to 't') diff --git a/daemon.c b/daemon.c index f285a8c98c..11fa3ed11f 100644 --- a/daemon.c +++ b/daemon.c @@ -142,7 +142,7 @@ static int upload(char *dir, int dirlen) * is ok with us doing this. */ if ((!export_all_trees && access("git-daemon-export-ok", F_OK)) || - access("objects/00", X_OK) || + access("objects/", X_OK) || access("HEAD", R_OK)) { logerror("Not a valid git-daemon-enabled repository: '%s'", dir); return -1; diff --git a/fsck-objects.c b/fsck-objects.c index 65cec7d12b..17d05363e0 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -329,9 +329,8 @@ static int fsck_dir(int i, char *path) DIR *dir = opendir(path); struct dirent *de; - if (!dir) { - return error("missing sha1 directory '%s'", path); - } + if (!dir) + return 0; while ((de = readdir(dir)) != NULL) { char name[100]; diff --git a/git-rename.perl b/git-rename.perl index a28c8c83bb..3b1127b1b2 100755 --- a/git-rename.perl +++ b/git-rename.perl @@ -15,7 +15,7 @@ sub usage($); my $GIT_DIR = $ENV{'GIT_DIR'} || ".git"; unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && - -d $GIT_DIR . "/objects/00" && -d $GIT_DIR . "/refs") { + -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") { usage("Git repository not found."); } diff --git a/git-sh-setup.sh b/git-sh-setup.sh index a0172686a9..dbb98842bf 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -22,4 +22,4 @@ refs/*) : ;; *) false ;; esac && [ -d "$GIT_DIR/refs" ] && -[ -d "$GIT_OBJECT_DIRECTORY/00" ] +[ -d "$GIT_OBJECT_DIRECTORY/" ] diff --git a/prune-packed.c b/prune-packed.c index 5306e8e5ef..73f0f3a462 100644 --- a/prune-packed.c +++ b/prune-packed.c @@ -26,6 +26,9 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len) else if (unlink(pathname) < 0) error("unable to unlink %s", pathname); } + pathname[len] = 0; + if (rmdir(pathname)) + mkdir(pathname, 0777); } static void prune_packed_objects(void) @@ -46,7 +49,7 @@ static void prune_packed_objects(void) sprintf(pathname + len, "%02x/", i); d = opendir(pathname); if (!d) - die("unable to open %s", pathname); + continue; prune_dir(i, d, pathname, len + 3); closedir(d); } diff --git a/sha1_file.c b/sha1_file.c index 287f618827..baaa4c00da 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1248,6 +1248,73 @@ char *write_sha1_file_prepare(void *buf, return sha1_file_name(sha1); } +/* + * Link the tempfile to the final place, possibly creating the + * last directory level as you do so. + * + * Returns the errno on failure, 0 on success. + */ +static int link_temp_to_file(const char *tmpfile, char *filename) +{ + int ret; + + if (!link(tmpfile, filename)) + return 0; + + /* + * Try to mkdir the last path component if that failed + * with an ENOENT. + * + * Re-try the "link()" regardless of whether the mkdir + * succeeds, since a race might mean that somebody + * else succeeded. + */ + ret = errno; + if (ret == ENOENT) { + char *dir = strrchr(filename, '/'); + if (dir) { + *dir = 0; + mkdir(filename, 0777); + *dir = '/'; + if (!link(tmpfile, filename)) + return 0; + ret = errno; + } + } + return ret; +} + +/* + * Move the just written object into its final resting place + */ +static int move_temp_to_file(const char *tmpfile, char *filename) +{ + int ret = link_temp_to_file(tmpfile, filename); + if (ret) { + /* + * 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. + * + * When this succeeds, we just return 0. We have nothing + * left to unlink. + */ + if (ret == EXDEV && !rename(tmpfile, filename)) + return 0; + } + unlink(tmpfile); + if (ret) { + if (ret != EEXIST) { + fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret)); + return -1; + } + /* FIXME!!! Collision check here ? */ + } + + return 0; +} + int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; @@ -1257,7 +1324,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha char *filename; static char tmpfile[PATH_MAX]; unsigned char hdr[50]; - int fd, hdrlen, ret; + int fd, hdrlen; /* Normally if we have it in the pack then we do not bother writing * it out into .git/objects/??/?{38} file. @@ -1320,32 +1387,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha close(fd); free(compressed); - ret = link(tmpfile, filename); - if (ret < 0) { - 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. - * - * When this succeeds, we just return 0. We have nothing - * left to unlink. - */ - if (ret == EXDEV && !rename(tmpfile, filename)) - return 0; - } - unlink(tmpfile); - if (ret) { - if (ret != EEXIST) { - fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret)); - return -1; - } - /* FIXME!!! Collision check here ? */ - } - - return 0; + return move_temp_to_file(tmpfile, filename); } int write_sha1_to_fd(int fd, const unsigned char *sha1) @@ -1420,8 +1462,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1) int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, size_t bufsize, size_t *bufposn) { - char *filename = sha1_file_name(sha1); - + char tmpfile[PATH_MAX]; int local; z_stream stream; unsigned char real_sha1[20]; @@ -1429,10 +1470,11 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, int ret; SHA_CTX c; - local = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); + snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory()); + local = mkstemp(tmpfile); if (local < 0) - return error("Couldn't open %s\n", filename); + return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1)); memset(&stream, 0, sizeof(stream)); @@ -1462,7 +1504,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, size = read(fd, buffer + *bufposn, bufsize - *bufposn); if (size <= 0) { close(local); - unlink(filename); + unlink(tmpfile); if (!size) return error("Connection closed?"); perror("Reading from connection"); @@ -1475,15 +1517,15 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, close(local); SHA1_Final(real_sha1, &c); if (ret != Z_STREAM_END) { - unlink(filename); + unlink(tmpfile); return error("File %s corrupted", sha1_to_hex(sha1)); } if (memcmp(sha1, real_sha1, 20)) { - unlink(filename); + unlink(tmpfile); return error("File %s has bad hash\n", sha1_to_hex(sha1)); } - - return 0; + + return move_temp_to_file(tmpfile, sha1_file_name(sha1)); } int has_pack_index(const unsigned char *sha1) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index bd940bd09b..5c5f854858 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -28,7 +28,7 @@ test_expect_success \ '.git/objects should be empty after git-init-db in an empty repo.' \ 'cmp -s /dev/null should-be-empty' -# also it should have 258 subdirectories; 256 fan-out, pack, and info. +# also it should have 258 subdirectories; 256 fan-out anymore, pack, and info. # 259 is counting "objects" itself find .git/objects -type d -print >full-of-directories test_expect_success \ -- cgit v1.3-5-g45d5 From 4769948afe7c502d37746edc2ee2c084c9dcb325 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 10 Oct 2005 13:50:01 -0700 Subject: Deal with $(bindir) and friends with whitespaces. ... using HPA's shellquote macro. Signed-off-by: Junio C Hamano --- Makefile | 34 ++++++++++++++++++++-------------- git-merge-recursive.py | 2 +- t/Makefile | 8 +++++++- templates/Makefile | 10 ++++++++-- 4 files changed, 36 insertions(+), 18 deletions(-) (limited to 't') diff --git a/Makefile b/Makefile index ac384c7dc4..f7eee4708a 100644 --- a/Makefile +++ b/Makefile @@ -163,6 +163,12 @@ LIB_OBJS = \ LIBS = $(LIB_FILE) LIBS += -lz +# Shell quote; +# Result of this needs to be placed inside '' +shq = $(subst ','\'',$(1)) +# This has surrounding '' +shellquote = '$(call shq,$(1))' + # # Platform specific tweaks # @@ -235,7 +241,7 @@ ifndef NO_OPENSSL OPENSSL_LINK = endif else - DEFINES += '-DNO_OPENSSL' + DEFINES += -DNO_OPENSSL MOZILLA_SHA1 = 1 OPENSSL_LIBSSL = endif @@ -294,7 +300,7 @@ endif endif endif -DEFINES += '-DSHA1_HEADER=$(SHA1_HEADER)' +DEFINES += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ @@ -311,7 +317,7 @@ all: git: git.sh Makefile rm -f $@+ $@ - sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \ + sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@X@@/$(X)/g' \ $(GIT_LIST_TWEAK) <$@.sh >$@+ @@ -320,22 +326,22 @@ git: git.sh Makefile $(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh rm -f $@ - sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \ + sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ $@.sh >$@ chmod +x $@ $(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl rm -f $@ - sed -e '1s|#!.*perl|#!$(PERL_PATH)|' \ + sed -e '1s|#!.*perl|#!$(call shq,$(PERL_PATH))|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ $@.perl >$@ chmod +x $@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py rm -f $@ - sed -e '1s|#!.*python|#!$(PYTHON_PATH)|' \ - -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR)|g' \ + sed -e '1s|#!.*python|#!$(call shq,$(PYTHON_PATH))|' \ + -e 's|@@GIT_PYTHON_PATH@@|$(call shq,$(GIT_PYTHON_DIR))|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ $@.py >$@ chmod +x $@ @@ -365,7 +371,7 @@ git-rev-list$X: LIBS += $(OPENSSL_LIBSSL) init-db.o: init-db.c $(CC) -c $(ALL_CFLAGS) \ - -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir)"' $*.c + -DDEFAULT_GIT_TEMPLATE_DIR=$(call shellquote,"$(template_dir)") $*.c $(LIB_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) @@ -397,13 +403,13 @@ check: ### Installation rules install: $(PROGRAMS) $(SCRIPTS) - $(INSTALL) -d -m755 $(DESTDIR)$(bindir) - $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(DESTDIR)$(bindir) - $(INSTALL) git-revert $(DESTDIR)$(bindir)/git-cherry-pick - sh ./cmd-rename.sh $(DESTDIR)$(bindir) + $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir)) + $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir)) + $(INSTALL) git-revert $(call shellquote,$(DESTDIR)$(bindir)/git-cherry-pick) + sh ./cmd-rename.sh $(call shellquote,$(DESTDIR)$(bindir)) $(MAKE) -C templates install - $(INSTALL) -d -m755 $(DESTDIR)$(GIT_PYTHON_DIR) - $(INSTALL) $(PYMODULES) $(DESTDIR)$(GIT_PYTHON_DIR) + $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) + $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) install-doc: $(MAKE) -C Documentation install diff --git a/git-merge-recursive.py b/git-merge-recursive.py index b80a860357..626d85493a 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -4,7 +4,7 @@ import sys, math, random, os, re, signal, tempfile, stat, errno, traceback from heapq import heappush, heappop from sets import Set -sys.path.append('@@GIT_PYTHON_PATH@@') +sys.path.append('''@@GIT_PYTHON_PATH@@''') from gitMergeCommon import * originalIndexFile = os.environ.get('GIT_INDEX_FILE', diff --git a/t/Makefile b/t/Makefile index e71da7782e..5c76afff83 100644 --- a/t/Makefile +++ b/t/Makefile @@ -7,10 +7,16 @@ SHELL_PATH ?= $(SHELL) TAR ?= $(TAR) +# Shell quote; +# Result of this needs to be placed inside '' +shq = $(subst ','\'',$(1)) +# This has surrounding '' +shellquote = '$(call shq,$(1))' + T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) all: - @$(foreach t,$T,echo "*** $t ***"; $(SHELL_PATH) $t $(GIT_TEST_OPTS) || exit; ) + @$(foreach t,$T,echo "*** $t ***"; $(call shellquote,$(SHELL_PATH)) $t $(GIT_TEST_OPTS) || exit; ) @rm -fr trash clean: diff --git a/templates/Makefile b/templates/Makefile index c23aee866d..07e928e56d 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -6,6 +6,12 @@ prefix ?= $(HOME) template_dir ?= $(prefix)/share/git-core/templates/ # DESTDIR= +# Shell quote; +# Result of this needs to be placed inside '' +shq = $(subst ','\'',$(1)) +# This has surrounding '' +shellquote = '$(call shq,$(1))' + all: boilerplates.made custom find blt @@ -38,6 +44,6 @@ clean: rm -rf blt boilerplates.made install: all - $(INSTALL) -d -m755 $(DESTDIR)$(template_dir) + $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(template_dir)) (cd blt && $(TAR) cf - .) | \ - (cd $(DESTDIR)$(template_dir) && $(TAR) xf -) + (cd $(call shellquote,$(DESTDIR)$(template_dir)) && $(TAR) xf -) -- cgit v1.3-5-g45d5 From 0a81552e06037a57eeb1f784564a3ac092f60dfb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 11 Oct 2005 15:15:15 -0700 Subject: Use git-update-ref and git-symbolic-ref in tests This makes all tests pass on cygwin. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t5000-tar-tree.sh | 6 +++--- t/t6001-rev-list-merge-order.sh | 2 +- t/t6002-rev-list-bisect.sh | 2 +- t/t6003-rev-list-topo-order.sh | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 't') diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 5dffb8e1e5..4db1bb1142 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -41,8 +41,8 @@ test_expect_success \ find a -type l | xargs git-update-index --add && treeid=`git-write-tree` && echo $treeid >treeid && - TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ - git-commit-tree $treeid .git/HEAD' + git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ + git-commit-tree $treeid b.commitid && - diff .git/HEAD b.commitid' + diff .git/$(git-symbolic-ref HEAD) b.commitid' test_expect_success \ 'extract tar archive' \ diff --git a/t/t6001-rev-list-merge-order.sh b/t/t6001-rev-list-merge-order.sh index 0101242382..8ec9ebb98a 100755 --- a/t/t6001-rev-list-merge-order.sh +++ b/t/t6001-rev-list-merge-order.sh @@ -108,7 +108,7 @@ save_tag h2 unique_commit g4 tree -p g2 save_tag g3 unique_commit g5 tree -p g2 save_tag g4 unique_commit g6 tree -p g3 -p h2 -tag l5 > .git/HEAD +git-update-ref HEAD $(tag l5) test_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -s " "' < .git/HEAD +git-update-ref HEAD $(tag l5) # E diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index 88d14ee1a3..3c4c44c24d 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -77,7 +77,7 @@ save_tag h2 unique_commit g4 tree -p g2 save_tag g3 unique_commit g5 tree -p g2 save_tag g4 unique_commit g6 tree -p g3 -p h2 -tag l5 > .git/HEAD +git-update-ref HEAD $(tag l5) test_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -s " "' < Date: Wed, 12 Oct 2005 12:01:31 -0700 Subject: Add git-index-pack utility git-index-pack builds a pack index file for an existing packed archive. With this utility a packed archive which was transferred without the corresponding pack index can be added to objects/pack/ without repacking. Signed-off-by: Sergey Vlasov Signed-off-by: Junio C Hamano --- Documentation/git-index-pack.txt | 44 ++++ Documentation/git.txt | 3 + Makefile | 2 +- index-pack.c | 451 +++++++++++++++++++++++++++++++++++++++ t/t5300-pack-object.sh | 18 ++ 5 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 Documentation/git-index-pack.txt create mode 100644 index-pack.c (limited to 't') diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt new file mode 100644 index 0000000000..71ce557276 --- /dev/null +++ b/Documentation/git-index-pack.txt @@ -0,0 +1,44 @@ +git-index-pack(1) +================= + +NAME +---- +git-index-pack - Build pack index file for an existing packed archive + + +SYNOPSIS +-------- +'git-index-pack' [-o ] + + +DESCRIPTION +----------- +Reads a packed archive (.pack) from the specified file, and +builds a pack index file (.idx) for it. The packed archive +together with the pack index can then be placed in the +objects/pack/ directory of a git repository. + + +OPTIONS +------- +-o :: + Write the generated pack index into the specified + file. Without this option the name of pack index + file is constructed from the name of packed archive + file by replacing .pack with .idx (and the program + fails if the name of packed archive does not end + with .pack). + + +Author +------ +Written by Sergey Vlasov + +Documentation +------------- +Documentation by Sergey Vlasov + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git.txt b/Documentation/git.txt index 243c00a178..796c4f61ea 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -68,6 +68,9 @@ gitlink:git-commit-tree[1]:: gitlink:git-hash-object[1]:: Computes the object ID from a file. +gitlink:git-index-pack.html[1]:: + Build pack index file for an existing packed archive. + gitlink:git-init-db[1]:: Creates an empty git object database diff --git a/Makefile b/Makefile index 5e7d0555ea..7c8f6474a3 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,7 @@ PROGRAMS = \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ - git-hash-object$X git-init-db$X \ + git-hash-object$X git-index-pack$X git-init-db$X \ git-local-fetch$X git-ls-files$X git-ls-tree$X git-merge-base$X \ git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ diff --git a/index-pack.c b/index-pack.c new file mode 100644 index 0000000000..badbeab70e --- /dev/null +++ b/index-pack.c @@ -0,0 +1,451 @@ +#include "cache.h" +#include "delta.h" +#include "pack.h" +#include "csum-file.h" + +static const char index_pack_usage[] = +"git-index-pack [-o index-file] pack-file"; + +struct object_entry +{ + unsigned long offset; + enum object_type type; + enum object_type real_type; + unsigned char sha1[20]; +}; + +struct delta_entry +{ + struct object_entry *obj; + unsigned char base_sha1[20]; +}; + +static const char *pack_name; +static unsigned char *pack_base; +static unsigned long pack_size; +static struct object_entry *objects; +static struct delta_entry *deltas; +static int nr_objects; +static int nr_deltas; + +static void open_pack_file(void) +{ + int fd; + struct stat st; + + fd = open(pack_name, O_RDONLY); + if (fd < 0) + die("cannot open packfile '%s': %s", pack_name, + strerror(errno)); + if (fstat(fd, &st)) { + int err = errno; + close(fd); + die("cannot fstat packfile '%s': %s", pack_name, + strerror(err)); + } + pack_size = st.st_size; + pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (pack_base == MAP_FAILED) { + int err = errno; + close(fd); + die("cannot mmap packfile '%s': %s", pack_name, + strerror(err)); + } + close(fd); +} + +static void parse_pack_header(void) +{ + const struct pack_header *hdr; + unsigned char sha1[20]; + SHA_CTX ctx; + + /* Ensure there are enough bytes for the header and final SHA1 */ + if (pack_size < sizeof(struct pack_header) + 20) + die("packfile '%s' is too small", pack_name); + + /* Header consistency check */ + hdr = (void *)pack_base; + if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) + die("packfile '%s' signature mismatch", pack_name); + if (hdr->hdr_version != htonl(PACK_VERSION)) + die("packfile '%s' version %d different from ours %d", + pack_name, ntohl(hdr->hdr_version), PACK_VERSION); + + nr_objects = ntohl(hdr->hdr_entries); + + /* Check packfile integrity */ + SHA1_Init(&ctx); + SHA1_Update(&ctx, pack_base, pack_size - 20); + SHA1_Final(sha1, &ctx); + if (memcmp(sha1, pack_base + pack_size - 20, 20)) + die("packfile '%s' SHA1 mismatch", pack_name); +} + +static void bad_object(unsigned long offset, const char *format, + ...) NORETURN __attribute__((format (printf, 2, 3))); + +static void bad_object(unsigned long offset, const char *format, ...) +{ + va_list params; + char buf[1024]; + + va_start(params, format); + vsnprintf(buf, sizeof(buf), format, params); + va_end(params); + die("packfile '%s': bad object at offset %lu: %s", + pack_name, offset, buf); +} + +static void *unpack_entry_data(unsigned long offset, + unsigned long *current_pos, unsigned long size) +{ + unsigned long pack_limit = pack_size - 20; + unsigned long pos = *current_pos; + z_stream stream; + void *buf = xmalloc(size); + + memset(&stream, 0, sizeof(stream)); + stream.next_out = buf; + stream.avail_out = size; + stream.next_in = pack_base + pos; + stream.avail_in = pack_limit - pos; + inflateInit(&stream); + + for (;;) { + int ret = inflate(&stream, 0); + if (ret == Z_STREAM_END) + break; + if (ret != Z_OK) + bad_object(offset, "inflate returned %d", ret); + } + inflateEnd(&stream); + if (stream.total_out != size) + bad_object(offset, "size mismatch (expected %lu, got %lu)", + size, stream.total_out); + *current_pos = pack_limit - stream.avail_in; + return buf; +} + +static void *unpack_raw_entry(unsigned long offset, + enum object_type *obj_type, + unsigned long *obj_size, + unsigned char *delta_base, + unsigned long *next_obj_offset) +{ + unsigned long pack_limit = pack_size - 20; + unsigned long pos = offset; + unsigned char c; + unsigned long size; + unsigned shift; + enum object_type type; + void *data; + + c = pack_base[pos++]; + type = (c >> 4) & 7; + size = (c & 15); + shift = 4; + while (c & 0x80) { + if (pos >= pack_limit) + bad_object(offset, "object extends past end of pack"); + c = pack_base[pos++]; + size += (c & 0x7fUL) << shift; + shift += 7; + } + + switch (type) { + case OBJ_DELTA: + if (pos + 20 >= pack_limit) + bad_object(offset, "object extends past end of pack"); + memcpy(delta_base, pack_base + pos, 20); + pos += 20; + /* fallthru */ + case OBJ_COMMIT: + case OBJ_TREE: + case OBJ_BLOB: + case OBJ_TAG: + data = unpack_entry_data(offset, &pos, size); + break; + default: + bad_object(offset, "bad object type %d", type); + } + + *obj_type = type; + *obj_size = size; + *next_obj_offset = pos; + return data; +} + +static int find_delta(const unsigned char *base_sha1) +{ + int first = 0, last = nr_deltas; + + while (first < last) { + int next = (first + last) / 2; + struct delta_entry *delta = &deltas[next]; + int cmp; + + cmp = memcmp(base_sha1, delta->base_sha1, 20); + if (!cmp) + return next; + if (cmp < 0) { + last = next; + continue; + } + first = next+1; + } + return -first-1; +} + +static int find_deltas_based_on_sha1(const unsigned char *base_sha1, + int *first_index, int *last_index) +{ + int first = find_delta(base_sha1); + int last = first; + int end = nr_deltas - 1; + + if (first < 0) + return -1; + while (first > 0 && !memcmp(deltas[first-1].base_sha1, base_sha1, 20)) + --first; + while (last < end && !memcmp(deltas[last+1].base_sha1, base_sha1, 20)) + ++last; + *first_index = first; + *last_index = last; + return 0; +} + +static void sha1_object(const void *data, unsigned long size, + enum object_type type, unsigned char *sha1) +{ + SHA_CTX ctx; + char header[50]; + int header_size; + const char *type_str; + + switch (type) { + case OBJ_COMMIT: type_str = "commit"; break; + case OBJ_TREE: type_str = "tree"; break; + case OBJ_BLOB: type_str = "blob"; break; + case OBJ_TAG: type_str = "tag"; break; + default: + die("bad type %d", type); + } + + header_size = sprintf(header, "%s %lu", type_str, size) + 1; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, header, header_size); + SHA1_Update(&ctx, data, size); + SHA1_Final(sha1, &ctx); +} + +static void resolve_delta(struct delta_entry *delta, void *base_data, + unsigned long base_size, enum object_type type) +{ + struct object_entry *obj = delta->obj; + void *delta_data; + unsigned long delta_size; + void *result; + unsigned long result_size; + enum object_type delta_type; + unsigned char base_sha1[20]; + unsigned long next_obj_offset; + int j, first, last; + + obj->real_type = type; + delta_data = unpack_raw_entry(obj->offset, &delta_type, + &delta_size, base_sha1, + &next_obj_offset); + result = patch_delta(base_data, base_size, delta_data, delta_size, + &result_size); + free(delta_data); + if (!result) + bad_object(obj->offset, "failed to apply delta"); + sha1_object(result, result_size, type, obj->sha1); + if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) { + for (j = first; j <= last; j++) + resolve_delta(&deltas[j], result, result_size, type); + } + free(result); +} + +static int compare_delta_entry(const void *a, const void *b) +{ + const struct delta_entry *delta_a = a; + const struct delta_entry *delta_b = b; + return memcmp(delta_a->base_sha1, delta_b->base_sha1, 20); +} + +static void parse_pack_objects(void) +{ + int i; + unsigned long offset = sizeof(struct pack_header); + unsigned char base_sha1[20]; + void *data; + unsigned long data_size; + + /* + * First pass: + * - find locations of all objects; + * - calculate SHA1 of all non-delta objects; + * - remember base SHA1 for all deltas. + */ + for (i = 0; i < nr_objects; i++) { + struct object_entry *obj = &objects[i]; + obj->offset = offset; + data = unpack_raw_entry(offset, &obj->type, &data_size, + base_sha1, &offset); + obj->real_type = obj->type; + if (obj->type == OBJ_DELTA) { + struct delta_entry *delta = &deltas[nr_deltas++]; + delta->obj = obj; + memcpy(delta->base_sha1, base_sha1, 20); + } else + sha1_object(data, data_size, obj->type, obj->sha1); + free(data); + } + if (offset != pack_size - 20) + die("packfile '%s' has junk at the end", pack_name); + + /* Sort deltas by base SHA1 for fast searching */ + qsort(deltas, nr_deltas, sizeof(struct delta_entry), + compare_delta_entry); + + /* + * Second pass: + * - for all non-delta objects, look if it is used as a base for + * deltas; + * - if used as a base, uncompress the object and apply all deltas, + * recursively checking if the resulting object is used as a base + * for some more deltas. + */ + for (i = 0; i < nr_objects; i++) { + struct object_entry *obj = &objects[i]; + int j, first, last; + + if (obj->type == OBJ_DELTA) + continue; + if (find_deltas_based_on_sha1(obj->sha1, &first, &last)) + continue; + data = unpack_raw_entry(obj->offset, &obj->type, &data_size, + base_sha1, &offset); + for (j = first; j <= last; j++) + resolve_delta(&deltas[j], data, data_size, obj->type); + free(data); + } + + /* Check for unresolved deltas */ + for (i = 0; i < nr_deltas; i++) { + if (deltas[i].obj->real_type == OBJ_DELTA) + die("packfile '%s' has unresolved deltas", pack_name); + } +} + +static int sha1_compare(const void *_a, const void *_b) +{ + struct object_entry *a = *(struct object_entry **)_a; + struct object_entry *b = *(struct object_entry **)_b; + return memcmp(a->sha1, b->sha1, 20); +} + +static void write_index_file(const char *index_name) +{ + struct sha1file *f; + struct object_entry **sorted_by_sha = + xcalloc(nr_objects, sizeof(struct object_entry *)); + struct object_entry **list = sorted_by_sha; + struct object_entry **last = sorted_by_sha + nr_objects; + unsigned int array[256]; + int i; + + for (i = 0; i < nr_objects; ++i) + sorted_by_sha[i] = &objects[i]; + qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]), + sha1_compare); + + unlink(index_name); + f = sha1create("%s", index_name); + + /* + * Write the first-level table (the list is sorted, + * but we use a 256-entry lookup to be able to avoid + * having to do eight extra binary search iterations). + */ + for (i = 0; i < 256; i++) { + struct object_entry **next = list; + while (next < last) { + struct object_entry *obj = *next; + if (obj->sha1[0] != i) + break; + next++; + } + array[i] = htonl(next - sorted_by_sha); + list = next; + } + sha1write(f, array, 256 * sizeof(int)); + + /* + * Write the actual SHA1 entries.. + */ + list = sorted_by_sha; + for (i = 0; i < nr_objects; i++) { + struct object_entry *obj = *list++; + unsigned int offset = htonl(obj->offset); + sha1write(f, &offset, 4); + sha1write(f, obj->sha1, 20); + } + sha1write(f, pack_base + pack_size - 20, 20); + sha1close(f, NULL, 1); + free(sorted_by_sha); +} + +int main(int argc, char **argv) +{ + int i; + char *index_name = NULL; + char *index_name_buf = NULL; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (*arg == '-') { + if (!strcmp(arg, "-o")) { + if (index_name || (i+1) >= argc) + usage(index_pack_usage); + index_name = argv[++i]; + } else + usage(index_pack_usage); + continue; + } + + if (pack_name) + usage(index_pack_usage); + pack_name = arg; + } + + if (!pack_name) + usage(index_pack_usage); + if (!index_name) { + int len = strlen(pack_name); + if (len < 5 || strcmp(pack_name + len - 5, ".pack")) + die("packfile name '%s' does not end with '.pack'", + pack_name); + index_name_buf = xmalloc(len - 1); + memcpy(index_name_buf, pack_name, len - 5); + strcpy(index_name_buf + len - 5, ".idx"); + index_name = index_name_buf; + } + + open_pack_file(); + parse_pack_header(); + objects = xcalloc(nr_objects, sizeof(struct object_entry)); + deltas = xcalloc(nr_objects, sizeof(struct delta_entry)); + parse_pack_objects(); + free(deltas); + write_index_file(index_name); + free(objects); + free(index_name_buf); + + return 0; +} diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index bb62336f26..96db98b65c 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -165,4 +165,22 @@ test_expect_success \ :' +test_expect_success \ + 'build pack index for an existing pack' \ + 'cp test-1-${packname_1}.pack test-3.pack && + git-index-pack -o tmp.idx test-3.pack && + cmp tmp.idx test-1-${packname_1}.idx && + + git-index-pack test-3.pack && + cmp test-3.idx test-1-${packname_1}.idx && + + cp test-2-${packname_2}.pack test-3.pack && + git-index-pack -o tmp.idx test-2-${packname_2}.pack && + cmp tmp.idx test-2-${packname_2}.idx && + + git-index-pack test-3.pack && + cmp test-3.idx test-2-${packname_2}.idx && + + :' + test_done -- cgit v1.3-5-g45d5 From c09a69a83e3edcc634f17cdc7b4c71b91f3228ce Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 16 Oct 2005 00:24:34 -0700 Subject: Disable hooks during tests. Individual tests for hooks would want to have their own tests when written. Also we should not pick up from random templates the user happens to have. Signed-off-by: Junio C Hamano --- t/test-lib.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 't') diff --git a/t/test-lib.sh b/t/test-lib.sh index 3f8a6fe414..a8f239df8f 100755 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -164,4 +164,8 @@ test=trash rm -fr "$test" mkdir "$test" cd "$test" -git-init-db 2>/dev/null || error "cannot run git-init-db" +git-init-db --template=../../templates/blt/ 2>/dev/null || +error "cannot run git-init-db" + +mv .git/hooks .git/hooks-disabled + -- cgit v1.3-5-g45d5 From 4d2060efebe855e49365c7be117b80c1e486a6b4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 15 Oct 2005 16:15:49 -0700 Subject: Add tests for funny pathnames. Signed-off-by: Junio C Hamano --- t/t3300-funny-names.sh | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100755 t/t3300-funny-names.sh (limited to 't') diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh new file mode 100755 index 0000000000..7e08eefd64 --- /dev/null +++ b/t/t3300-funny-names.sh @@ -0,0 +1,133 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +test_description='Pathnames with funny characters. + +This test tries pathnames with funny characters in the working +tree, index, and tree objects. +' + +. ./test-lib.sh + +p0='no-funny' +p1='tabs and spaces' + +cat >"$p0" <<\EOF +1. A quick brown fox jumps over the lazy cat, oops dog. +2. A quick brown fox jumps over the lazy cat, oops dog. +3. A quick brown fox jumps over the lazy cat, oops dog. +EOF + +cat >"$p1" "$p0" + +echo 'no-funny' >expected +test_expect_success 'git-ls-files no-funny' \ + 'git-update-index --add "$p0" && + git-ls-files >current && + diff -u expected current' + +t0=`git-write-tree` +echo "$t0" >t0 + +echo 'no-funny +"tabs\tand spaces"' >expected +test_expect_success 'git-ls-files with-funny' \ + 'git-update-index --add "$p1" && + git-ls-files >current && + diff -u expected current' + +echo 'no-funny +tabs and spaces' >expected +test_expect_success 'git-ls-files -z with-funny' \ + 'git-ls-files -z | tr \\0 \\012 >current && + diff -u expected current' + +t1=`git-write-tree` +echo "$t1" >t1 + +echo 'no-funny +"tabs\tand spaces"' >expected +test_expect_success 'git-ls-tree with funny' \ + 'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current && + diff -u expected current' + +echo 'A "tabs\tand spaces"' >expected +test_expect_success 'git-diff-index with-funny' \ + 'git-diff-index --name-status $t0 >current && + diff -u expected current' + +test_expect_success 'git-diff-tree with-funny' \ + 'git-diff-tree --name-status $t0 $t1 >current && + diff -u expected current' + +echo 'A +tabs and spaces' >expected +test_expect_success 'git-diff-index -z with-funny' \ + 'git-diff-index -z --name-status $t0 | tr \\0 \\012 >current && + diff -u expected current' + +test_expect_success 'git-diff-tree -z with-funny' \ + 'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current && + diff -u expected current' + +echo 'CNUM no-funny "tabs\tand spaces"' >expected +test_expect_success 'git-diff-tree -C with-funny' \ + 'git-diff-tree -C --find-copies-harder --name-status \ + $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current && + diff -u expected current' + +echo 'RNUM no-funny "tabs\tand spaces"' >expected +test_expect_success 'git-diff-tree delete with-funny' \ + 'git-update-index --force-remove "$p0" && + git-diff-index -M --name-status \ + $t0 | sed -e 's/^R[0-9]*/RNUM/' >current && + diff -u expected current' + +echo 'diff --git a/no-funny "b/tabs\tand spaces" +similarity index NUM% +rename from no-funny +rename to "tabs\tand spaces"' >expected + +test_expect_success 'git-diff-tree delete with-funny' \ + 'git-diff-index -M -p $t0 | + sed -e "s/index [0-9]*%/index NUM%/" >current && + diff -u expected current' + +chmod +x "$p1" +echo 'diff --git a/no-funny "b/tabs\tand spaces" +old mode 100644 +new mode 100755 +similarity index NUM% +rename from no-funny +rename to "tabs\tand spaces"' >expected + +test_expect_success 'git-diff-tree delete with-funny' \ + 'git-diff-index -M -p $t0 | + sed -e "s/index [0-9]*%/index NUM%/" >current && + diff -u expected current' + +echo >expected ' "tabs\tand spaces" + 1 files changed, 0 insertions(+), 0 deletions(-)' +test_expect_success 'git-diff-tree rename with-funny applied' \ + 'git-diff-index -M -p $t0 | + git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && + diff -u expected current' + +echo >expected ' no-funny + "tabs\tand spaces" + 2 files changed, 3 insertions(+), 3 deletions(-)' + +test_expect_success 'git-diff-tree delete with-funny applied' \ + 'git-diff-index -p $t0 | + git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && + diff -u expected current' + +test_expect_success 'git-apply non-git diff' \ + 'git-diff-index -p $t0 | + sed -ne "/^[-+@]/p" | + git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && + diff -u expected current' + +test_done -- cgit v1.3-5-g45d5 From 508c1d1c9b13f53783d024ccb28f77d0e6f10e7f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Oct 2005 13:32:14 -0700 Subject: Adjust tests for not quoting SP. Signed-off-by: Junio C Hamano --- t/t3300-funny-names.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 't') diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index 7e08eefd64..ccd7063e97 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -13,6 +13,7 @@ tree, index, and tree objects. p0='no-funny' p1='tabs and spaces' +p2='just space' cat >"$p0" <<\EOF 1. A quick brown fox jumps over the lazy cat, oops dog. @@ -21,24 +22,28 @@ cat >"$p0" <<\EOF EOF cat >"$p1" "$p0" +echo 'Foo Bar Baz' >"$p2" -echo 'no-funny' >expected +echo 'just space +no-funny' >expected test_expect_success 'git-ls-files no-funny' \ - 'git-update-index --add "$p0" && + 'git-update-index --add "$p0" "$p2" && git-ls-files >current && diff -u expected current' t0=`git-write-tree` echo "$t0" >t0 -echo 'no-funny +echo 'just space +no-funny "tabs\tand spaces"' >expected test_expect_success 'git-ls-files with-funny' \ 'git-update-index --add "$p1" && git-ls-files >current && diff -u expected current' -echo 'no-funny +echo 'just space +no-funny tabs and spaces' >expected test_expect_success 'git-ls-files -z with-funny' \ 'git-ls-files -z | tr \\0 \\012 >current && @@ -47,7 +52,8 @@ test_expect_success 'git-ls-files -z with-funny' \ t1=`git-write-tree` echo "$t1" >t1 -echo 'no-funny +echo 'just space +no-funny "tabs\tand spaces"' >expected test_expect_success 'git-ls-tree with funny' \ 'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current && -- cgit v1.3-5-g45d5 From 5f93926c3c5c4fe1def39de82076bc69ec89c058 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 18 Oct 2005 11:35:17 -0700 Subject: No funny names on cygwin... On FAT/NTFS, filenames cannot contain tabs. So t3300-funny-names would reliably fail already when trying to create such files. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3300-funny-names.sh | 3 +++ 1 file changed, 3 insertions(+) (limited to 't') diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index ccd7063e97..897c378422 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -9,6 +9,9 @@ This test tries pathnames with funny characters in the working tree, index, and tree objects. ' +# since FAT/NTFS does not allow tabs in filenames, skip this test +test "$(uname -o 2>/dev/null)" = Cygwin && exit 0 + . ./test-lib.sh p0='no-funny' -- cgit v1.3-5-g45d5 From 9106c097ad87577019544f45fda11c4d73986597 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 9 Oct 2005 02:30:17 -0700 Subject: Create object subdirectories on demand (phase II) This removes the unoptimization. The previous round does not mind missing fan-out directories, but still makes sure they exist, lest older versions choke on a repository created/packed by it. This round does not play that nicely anymore -- empty fan-out directories are not created by init-db, and will stay removed by prune-packed. The prune command also removes empty fan-out directories. Signed-off-by: Junio C Hamano --- git-prune.sh | 1 + init-db.c | 4 ---- prune-packed.c | 3 +-- t/t0000-basic.sh | 8 ++++---- 4 files changed, 6 insertions(+), 10 deletions(-) (limited to 't') diff --git a/git-prune.sh b/git-prune.sh index 9657dbf271..b28630cacf 100755 --- a/git-prune.sh +++ b/git-prune.sh @@ -22,6 +22,7 @@ sed -ne '/unreachable /{ }' | { cd "$GIT_OBJECT_DIRECTORY" || exit xargs $echo rm -f + rmdir 2>/dev/null [0-9a-f][0-9a-f] } git-prune-packed $dryrun diff --git a/init-db.c b/init-db.c index 2a4aa3c196..ca6fa4d420 100644 --- a/init-db.c +++ b/init-db.c @@ -281,10 +281,6 @@ int main(int argc, char **argv) memcpy(path, sha1_dir, len); safe_create_dir(sha1_dir); - for (i = 0; i < 256; i++) { - sprintf(path+len, "/%02x", i); - safe_create_dir(path); - } strcpy(path+len, "/pack"); safe_create_dir(path); strcpy(path+len, "/info"); diff --git a/prune-packed.c b/prune-packed.c index 1e0fc0cd9e..16685d1d8b 100644 --- a/prune-packed.c +++ b/prune-packed.c @@ -27,8 +27,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len) error("unable to unlink %s", pathname); } pathname[len] = 0; - if (!rmdir(pathname)) - mkdir(pathname, 0777); + rmdir(pathname); } static void prune_packed_objects(void) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 5c5f854858..dff7d69163 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -28,12 +28,12 @@ test_expect_success \ '.git/objects should be empty after git-init-db in an empty repo.' \ 'cmp -s /dev/null should-be-empty' -# also it should have 258 subdirectories; 256 fan-out anymore, pack, and info. -# 259 is counting "objects" itself +# also it should have 2 subdirectories; no fan-out anymore, pack, and info. +# 3 is counting "objects" itself find .git/objects -type d -print >full-of-directories test_expect_success \ - '.git/objects should have 258 subdirectories.' \ - 'test $(wc -l < full-of-directories) = 259' + '.git/objects should have 3 subdirectories.' \ + 'test $(wc -l < full-of-directories) = 3' ################################################################ # Basics of the basics -- cgit v1.3-5-g45d5 From f07a524195a73537d4187460ccbd072bcb1ff486 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Thu, 27 Oct 2005 23:00:43 -0400 Subject: fix testsuite to tolerate spaces in path This patch allows the testsuite to run properly when the full path to the git sources contains spaces or other symbols that need to be quoted. Signed-off-by: Pavel Roskin Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 't') diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 96db98b65c..5b50536b54 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -49,7 +49,7 @@ test_expect_success \ git-unpack-objects Date: Fri, 28 Oct 2005 04:47:38 +0200 Subject: Implement a test for git-fetch-pack/git-upload-pack This test provides a minimal example of what went wrong with the old git-fetch-pack (and now works beautifully). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t5500-fetch-pack.sh | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 t/t5500-fetch-pack.sh (limited to 't') diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh new file mode 100644 index 0000000000..0781bd287e --- /dev/null +++ b/t/t5500-fetch-pack.sh @@ -0,0 +1,136 @@ +#!/bin/sh +# +# Copyright (c) 2005 Johannes Schindelin +# + +test_description='Testing multi_ack pack fetching + +' +. ./test-lib.sh + +# Test fetch-pack/upload-pack pair. + +# Some convenience functions + +function show_count () { + commit_count=$(($commit_count+1)) + printf " %d\r" $commit_count +} + +function add () { + local name=$1 + local text="$@" + local branch=${name:0:1} + local parents="" + + shift + while test $1; do + parents="$parents -p $1" + shift + done + + echo "$text" > test.txt + git-update-index --add test.txt + tree=$(git-write-tree) + # make sure timestamps are in correct order + sec=$(($sec+1)) + commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \ + git-commit-tree $tree $parents 2>>log2.txt) + export $name=$commit + echo $commit > .git/refs/heads/$branch + eval ${branch}TIP=$commit +} + +function count_objects () { + ls .git/objects/??/* 2>>log2.txt | wc -l | tr -d " " +} + +function test_expect_object_count () { + local message=$1 + local count=$2 + + output="$(count_objects)" + test_expect_success \ + "new object count $message" \ + "test $count = $output" +} + +function test_repack () { + local rep=$1 + + test_expect_success "repack && prune-packed in $rep" \ + '(git-repack && git-prune-packed)2>>log.txt' +} + +function pull_to_client () { + local number=$1 + local heads=$2 + local count=$3 + local no_strict_count_check=$4 + + cd client + test_expect_success "$number pull" \ + "git-fetch-pack -v .. $heads > log.txt 2>&1" + case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac + case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac + git-symbolic-ref HEAD refs/heads/${heads:0:1} + test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1' + test_expect_object_count "after $number pull" $count + pack_count=$(grep Packing log.txt|tr -dc "0-9") + test -z "$pack_count" && pack_count=0 + if [ -z "$no_strict_count_check" ]; then + test_expect_success "minimal count" "test $count = $pack_count" + else + test $count != $pack_count && \ + echo "WARNING: $pack_count objects transmitted, only $count of which were needed" + fi + cd .. +} + +# Here begins the actual testing + +# A1 - ... - A20 - A21 +# \ +# B1 - B2 - .. - B70 + +# client pulls A20, B1. Then tracks only B. Then pulls A. + +( + mkdir client && + cd client && + git-init-db 2>> log2.txt +) + +add A1 + +prev=1; cur=2; while [ $cur -le 10 ]; do + add A$cur $(eval echo \$A$prev) + prev=$cur + cur=$(($cur+1)) +done + +add B1 $A1 + +echo $ATIP > .git/refs/heads/A +echo $BTIP > .git/refs/heads/B +git-symbolic-ref HEAD refs/heads/B + +pull_to_client 1st "B A" $((11*3)) + +(cd client; test_repack client) + +add A11 $A10 + +prev=1; cur=2; while [ $cur -le 65 ]; do + add B$cur $(eval echo \$B$prev) + prev=$cur + cur=$(($cur+1)) +done + +pull_to_client 2nd "B" $((64*3)) + +(cd client; test_repack client) + +pull_to_client 3rd "A" $((1*3)) # old fails + +test_done -- cgit v1.3-5-g45d5 From eebda31d21d5d5df351b41994df875918863294e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 28 Oct 2005 04:48:03 +0200 Subject: Implement an interoperability test for fetch-pack/upload-pack The next patches will extend the pack protocol. This test assures that this extension is compatible to earlier versions of git-fetch-pack/git-upload-pack. All you need to do to take advantage of this test, is to install older known-to-be-working binaries in the path as "old-git-fetch-pack" and "old-git-upload-pack". Note that the warning when testing with old-git-fetch-pack is to be expected (it just says that the old version was not taking advantage of all the information which the server sent). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t5501-old-fetch-and-upload.sh | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 t/t5501-old-fetch-and-upload.sh (limited to 't') diff --git a/t/t5501-old-fetch-and-upload.sh b/t/t5501-old-fetch-and-upload.sh new file mode 100755 index 0000000000..86df785495 --- /dev/null +++ b/t/t5501-old-fetch-and-upload.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Copyright (c) 2005 Johannes Schindelin +# + +# Test that the current fetch-pack/upload-pack plays nicely with +# an old counterpart + +cd $(dirname $0) || exit 1 + +tmp=$(mktemp tmp-XXXX) + +retval=0 + +if [ -z "$1" ]; then + list="fetch upload" +else + list="$@" +fi + +for i in $list; do + case "$i" in + fetch) pgm="old-git-fetch-pack"; replace="$pgm";; + upload) pgm="old-git-upload-pack"; replace="git-fetch-pack --exec=$pgm";; + both) pgm="old-git-upload-pack"; replace="old-git-fetch-pack --exec=$pgm";; + esac + + if which $pgm; then + echo "Testing with $pgm" + sed -e "s/git-fetch-pack/$replace/g" \ + -e "s/# old fails/warn/" < t5500-fetch-pack.sh > $tmp + + sh $tmp || retval=$? + rm $tmp + + test $retval != 0 && exit $retval + else + echo "Skipping test for $i, since I cannot find $pgm" + fi +done + +exit 0 + -- cgit v1.3-5-g45d5 From 8d7d1670a87743c699aa8a981506c952088dc2d4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 28 Oct 2005 05:59:29 +0200 Subject: make t5501 less annoying On Linux, "mktemp tmp-XXXX" will not work. Also, redirect stderr on which, so it does not complain too loudly. After all, this test should only be executed when old binaries are available. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t5501-old-fetch-and-upload.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 't') diff --git a/t/t5501-old-fetch-and-upload.sh b/t/t5501-old-fetch-and-upload.sh index 86df785495..ada5130328 100755 --- a/t/t5501-old-fetch-and-upload.sh +++ b/t/t5501-old-fetch-and-upload.sh @@ -8,7 +8,7 @@ cd $(dirname $0) || exit 1 -tmp=$(mktemp tmp-XXXX) +tmp=$(mktemp /tmp/tmp-XXXXXXXX) retval=0 @@ -25,7 +25,7 @@ for i in $list; do both) pgm="old-git-upload-pack"; replace="old-git-fetch-pack --exec=$pgm";; esac - if which $pgm; then + if which $pgm 2>/dev/null; then echo "Testing with $pgm" sed -e "s/git-fetch-pack/$replace/g" \ -e "s/# old fails/warn/" < t5500-fetch-pack.sh > $tmp -- cgit v1.3-5-g45d5