From: Shawn O. Pearce Date: Sun, 14 Jan 2007 07:44:18 +0000 (-0500) Subject: Merge branch 'master' into sp/fast-import X-Git-Tag: v1.5.0-rc4~14^2~44 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1fcdd62adf81a172f45c7c6a58177212d500b9d9?hp=-c Merge branch 'master' into sp/fast-import I'm bringing master in early so that the OBJ_OFS_DELTA implementation is available as part of the topic. This way git-fast-import can learn about this new slightly smaller and faster packfile format, and can generate them directly rather than needing to have them be repacked with git-pack-objects. Due to the API changes in master during the period of development of git-fast-import, a few minor tweaks to fast-import.c are needed to produce a working merge. I've done them here as part of the merge to ensure bisection always works. Signed-off-by: Shawn O. Pearce --- 1fcdd62adf81a172f45c7c6a58177212d500b9d9 diff --combined .gitignore index 8ddccd7dac,6da1cdbd0d..a9dc462cb5 --- a/.gitignore +++ b/.gitignore @@@ -2,13 -2,16 +2,16 @@@ GIT-CFLAG GIT-VERSION-FILE git git-add + git-add--interactive git-am git-annotate git-apply git-applymbox git-applypatch git-archimport + git-archive git-bisect + git-blame git-branch git-cat-file git-check-ref-format @@@ -32,13 -35,14 +35,15 @@@ git-diff-inde git-diff-stages git-diff-tree git-describe +git-fast-import git-fetch git-fetch-pack git-findtags git-fmt-merge-msg + git-for-each-ref git-format-patch git-fsck-objects + git-gc git-get-tar-commit-id git-grep git-hash-object @@@ -46,6 -50,7 +51,7 @@@ git-http-fetc git-http-push git-imap-send git-index-pack + git-init git-init-db git-instaweb git-local-fetch @@@ -59,10 -64,12 +65,12 @@@ git-mailspli git-merge git-merge-base git-merge-index + git-merge-file git-merge-tree git-merge-octopus git-merge-one-file git-merge-ours + git-merge-recur git-merge-recursive git-merge-resolve git-merge-stupid @@@ -72,6 -79,7 +80,7 @@@ git-name-re git-mv git-pack-redundant git-pack-objects + git-pack-refs git-parse-remote git-patch-id git-peek-remote @@@ -83,7 -91,9 +92,9 @@@ git-quiltimpor git-read-tree git-rebase git-receive-pack + git-reflog git-relink + git-remote git-repack git-repo-config git-request-pull @@@ -94,6 -104,7 +105,7 @@@ git-rev-lis git-rev-parse git-revert git-rm + git-runstatus git-send-email git-send-pack git-sh-setup @@@ -102,6 -113,7 +114,7 @@@ git-shortlo git-show git-show-branch git-show-index + git-show-ref git-ssh-fetch git-ssh-pull git-ssh-push @@@ -118,8 -130,8 +131,8 @@@ git-unpack-object git-update-index git-update-ref git-update-server-info + git-upload-archive git-upload-pack - git-upload-tar git-var git-verify-pack git-verify-tag @@@ -140,9 -152,9 +153,9 @@@ git-core.spe *.py[co] config.mak autom4te.cache + config.cache config.log config.status config.mak.autogen config.mak.append configure - git-blame diff --combined Makefile index a37f74a1ff,8432ab8ba1..db9d8ee4f4 --- a/Makefile +++ b/Makefile @@@ -1,11 -1,6 +1,6 @@@ # The default target of this Makefile is... - all: + all:: - # Define MOZILLA_SHA1 environment variable when running make to make use of - # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast - # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default - # choice) has very fast version optimized for i586. - # # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies MOZILLA_SHA1. # @@@ -60,6 -55,11 +55,11 @@@ # Define ARM_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for ARM. # + # Define MOZILLA_SHA1 environment variable when running make to make use of + # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast + # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default + # choice) has very fast version optimized for i586. + # # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin). # # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin). @@@ -69,7 -69,14 +69,14 @@@ # # Define NO_MMAP if you want to avoid mmap. # - # Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3. + # Define NO_PREAD if you have a problem with pread() system call (e.g. + # cygwin.dll before v1.5.22). + # + # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is + # generally faster on your platform than accessing the working directory. + # + # Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support + # the executable mode bit, but doesn't really do so. # # Define NO_IPV6 if you lack IPv6 support and getaddrinfo(). # @@@ -78,23 -85,22 +85,22 @@@ # # Define NO_ICONV if your libc does not properly support iconv. # - # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses - # a missing newline at the end of the file. - # - # Define NO_PYTHON if you want to lose all benefits of the recursive merge. + # Define NO_R_TO_GCC if your gcc does not like "-R/path/lib" that + # tells runtime paths to dynamic libraries; "-Wl,-rpath=/path/lib" + # is used instead. # - # Define COLLISION_CHECK below if you believe that SHA1's - # 1461501637330902918203684832716283019655932542976 hashes do not give you - # sufficient guarantee that no collisions between objects will ever happen. - # Define USE_NSEC below if you want git to care about sub-second file mtimes # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely # randomly break unless your underlying filesystem supports those sub-second # times (my ext3 doesn't). - + # # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-cache perspective. + # + # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's + # MakeMaker (e.g. using ActiveState under Cygwin). + # GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN @@@ -118,7 -124,6 +124,6 @@@ prefix = $(HOME bindir = $(prefix)/bin gitexecdir = $(bindir) template_dir = $(prefix)/share/git-core/templates/ - GIT_PYTHON_DIR = $(prefix)/share/git-core/python # DESTDIR= # default configuration for gitweb @@@ -126,13 -131,18 +131,18 @@@ GITWEB_CONFIG = gitweb_config.per GITWEB_HOME_LINK_STR = projects GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git + GITWEB_EXPORT_OK = + GITWEB_STRICT_EXPORT = GITWEB_BASE_URL = GITWEB_LIST = GITWEB_HOMETEXT = indextext.html GITWEB_CSS = gitweb.css GITWEB_LOGO = git-logo.png + GITWEB_FAVICON = git-favicon.png + GITWEB_SITE_HEADER = + GITWEB_SITE_FOOTER = - export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR + export prefix bindir gitexecdir template_dir CC = gcc AR = ar @@@ -148,10 -158,16 +158,16 @@@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__pow ### --- END CONFIGURATION SECTION --- + # Those must not be GNU-specific; they are shared with perl/ which may + # be built by a different compiler. (Note that this is an artifact now + # but it still might be nice to keep that distinction.) + BASIC_CFLAGS = + BASIC_LDFLAGS = + SCRIPT_SH = \ - git-bisect.sh git-branch.sh git-checkout.sh \ - git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ - git-fetch.sh \ + git-bisect.sh git-checkout.sh \ + git-clean.sh git-clone.sh git-commit.sh \ + git-fetch.sh git-gc.sh \ git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh \ @@@ -164,30 -180,22 +180,23 @@@ git-lost-found.sh git-quiltimport.sh SCRIPT_PERL = \ + git-add--interactive.perl \ git-archimport.perl git-cvsimport.perl git-relink.perl \ - git-shortlog.perl git-rerere.perl \ - git-annotate.perl git-cvsserver.perl \ + git-cvsserver.perl git-remote.perl \ git-svnimport.perl git-cvsexportcommit.perl \ git-send-email.perl git-svn.perl - SCRIPT_PYTHON = \ - git-merge-recursive.py - SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ git-cherry-pick git-status git-instaweb - # The ones that do not have to link with lcrypto, lz nor xdiff. - SIMPLE_PROGRAMS = \ - git-daemon$X - # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ + git-fast-import$X \ git-merge-base$X \ + git-daemon$X \ git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \ git-peek-remote$X git-receive-pack$X \ git-send-pack$X git-shell$X \ @@@ -196,19 -204,21 +205,21 @@@ git-update-server-info$X \ git-upload-pack$X git-verify-pack$X \ git-pack-redundant$X git-var$X \ - git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \ + git-merge-tree$X git-imap-send$X \ + git-merge-recursive$X \ $(EXTRA_PROGRAMS) # Empty... EXTRA_PROGRAMS = BUILT_INS = \ - git-format-patch$X git-show$X git-whatchanged$X \ - git-get-tar-commit-id$X \ + git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \ + git-get-tar-commit-id$X git-init$X \ $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) # what 'all' will build and 'install' will install, in gitexecdir - ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) + ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) \ + git-merge-recur$X # Backward compatibility -- to be removed after 1.0 PROGRAMS += git-ssh-pull$X git-ssh-push$X @@@ -220,21 -230,18 +231,18 @@@ endi ifndef PERL_PATH PERL_PATH = /usr/bin/perl endif - ifndef PYTHON_PATH - PYTHON_PATH = /usr/bin/python - endif - PYMODULES = \ - gitMergeCommon.py + export PERL_PATH LIB_FILE=libgit.a XDIFF_LIB=xdiff/lib.a LIB_H = \ - blob.h cache.h commit.h csum-file.h delta.h \ - diff.h object.h pack.h pkt-line.h quote.h refs.h \ + archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \ + diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ - tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h + tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ + utf8.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@@ -243,29 -250,39 +251,39 @@@ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ - date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \ - object.o pack-check.o patch-delta.o path.o pkt-line.o \ + date.o diff-delta.o entry.o exec_cmd.o ident.o \ + interpolate.o \ + lockfile.o \ + object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ + reachable.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ - fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - write_or_die.o \ - alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) + revision.o pager.o tree-walk.o xdiff-interface.o \ + write_or_die.o trace.o list-objects.o grep.o \ + alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ + color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o BUILTIN_OBJS = \ builtin-add.o \ + builtin-annotate.o \ builtin-apply.o \ + builtin-archive.o \ + builtin-blame.o \ + builtin-branch.o \ builtin-cat-file.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ builtin-commit-tree.o \ builtin-count-objects.o \ + builtin-describe.o \ builtin-diff.o \ builtin-diff-files.o \ builtin-diff-index.o \ builtin-diff-stages.o \ builtin-diff-tree.o \ builtin-fmt-merge-msg.o \ + builtin-for-each-ref.o \ builtin-grep.o \ builtin-init-db.o \ builtin-log.o \ @@@ -273,6 -290,7 +291,7 @@@ builtin-ls-tree.o \ builtin-mailinfo.o \ builtin-mailsplit.o \ + builtin-merge-file.o \ builtin-mv.o \ builtin-name-rev.o \ builtin-pack-objects.o \ @@@ -280,10 -298,14 +299,14 @@@ builtin-prune-packed.o \ builtin-push.o \ builtin-read-tree.o \ + builtin-reflog.o \ builtin-repo-config.o \ + builtin-rerere.o \ builtin-rev-list.o \ builtin-rev-parse.o \ builtin-rm.o \ + builtin-runstatus.o \ + builtin-shortlog.o \ builtin-show-branch.o \ builtin-stripspace.o \ builtin-symbolic-ref.o \ @@@ -291,12 -313,14 +314,14 @@@ builtin-unpack-objects.o \ builtin-update-index.o \ builtin-update-ref.o \ - builtin-upload-tar.o \ + builtin-upload-archive.o \ builtin-verify-pack.o \ - builtin-write-tree.o + builtin-write-tree.o \ + builtin-show-ref.o \ + builtin-pack-refs.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) - LIBS = $(GITLIBS) -lz + EXTLIBS = -lz # # Platform specific tweaks @@@ -316,18 -340,6 +341,6 @@@ ifeq ($(uname_S),Darwin NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease NO_STRLCPY = YesPlease - ifndef NO_FINK - ifeq ($(shell test -d /sw/lib && echo y),y) - ALL_CFLAGS += -I/sw/include - ALL_LDFLAGS += -L/sw/lib - endif - endif - ifndef NO_DARWIN_PORTS - ifeq ($(shell test -d /opt/local/lib && echo y),y) - ALL_CFLAGS += -I/opt/local/include - ALL_LDFLAGS += -L/opt/local/lib - endif - endif endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@@ -347,7 -359,7 +360,7 @@@ endif INSTALL = ginstall TAR = gtar - ALL_CFLAGS += -D__EXTENSIONS__ + BASIC_CFLAGS += -D__EXTENSIONS__ endif ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease @@@ -356,30 -368,33 +369,33 @@@ NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease NO_C99_FORMAT = YesPlease + NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes + NO_TRUSTABLE_FILEMODE = UnfortunatelyYes # There are conflicting reports about this. # On some boxes NO_MMAP is needed, and not so elsewhere. - # Try uncommenting this if you see things break -- YMMV. - # NO_MMAP = YesPlease + # Try commenting this out if you suspect MMAP is more efficient + NO_MMAP = YesPlease NO_IPV6 = YesPlease X = .exe endif ifeq ($(uname_S),FreeBSD) NEEDS_LIBICONV = YesPlease - ALL_CFLAGS += -I/usr/local/include - ALL_LDFLAGS += -L/usr/local/lib + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease NEEDS_LIBICONV = YesPlease - ALL_CFLAGS += -I/usr/local/include - ALL_LDFLAGS += -L/usr/local/lib + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib endif ifeq ($(uname_S),NetBSD) ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) NEEDS_LIBICONV = YesPlease endif - ALL_CFLAGS += -I/usr/pkg/include - ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib + BASIC_CFLAGS += -I/usr/pkg/include + BASIC_LDFLAGS += -L/usr/pkg/lib + ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease @@@ -393,9 -408,9 +409,9 @@@ ifeq ($(uname_S),IRIX64 NO_STRLCPY = YesPlease NO_SOCKADDR_STORAGE=YesPlease SHELL_PATH=/usr/gnu/bin/bash - ALL_CFLAGS += -DPATH_MAX=1024 + BASIC_CFLAGS += -DPATH_MAX=1024 # for now, build 32-bit version - ALL_LDFLAGS += -L/usr/lib32 + BASIC_LDFLAGS += -L/usr/lib32 endif ifneq (,$(findstring arm,$(uname_M))) ARM_SHA1 = YesPlease @@@ -404,21 -419,34 +420,34 @@@ endi -include config.mak.autogen -include config.mak - ifdef WITH_OWN_SUBPROCESS_PY - PYMODULES += compat/subprocess.py - else - ifeq ($(NO_PYTHON),) - ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK) - PYMODULES += compat/subprocess.py + ifeq ($(uname_S),Darwin) + ifndef NO_FINK + ifeq ($(shell test -d /sw/lib && echo y),y) + BASIC_CFLAGS += -I/sw/include + BASIC_LDFLAGS += -L/sw/lib + endif + endif + ifndef NO_DARWIN_PORTS + ifeq ($(shell test -d /opt/local/lib && echo y),y) + BASIC_CFLAGS += -I/opt/local/include + BASIC_LDFLAGS += -L/opt/local/lib endif endif endif + ifdef NO_R_TO_GCC_LINKER + # Some gcc does not accept and pass -R to the linker to specify + # the runtime dynamic library path. + CC_LD_DYNPATH = -Wl,-rpath= + else + CC_LD_DYNPATH = -R + endif + ifndef NO_CURL ifdef CURLDIR - # This is still problematic -- gcc does not always want -R. - ALL_CFLAGS += -I$(CURLDIR)/include - CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl + # Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case. + BASIC_CFLAGS += -I$(CURLDIR)/include + CURL_LIBCURL = -L$(CURLDIR)/lib $(CC_LD_DYNPATH)$(CURLDIR)/lib -lcurl else CURL_LIBCURL = -lcurl endif @@@ -437,14 -465,13 +466,13 @@@ endi ifndef NO_OPENSSL OPENSSL_LIBSSL = -lssl ifdef OPENSSLDIR - # Again this may be problematic -- gcc does not always want -R. - ALL_CFLAGS += -I$(OPENSSLDIR)/include - OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib + BASIC_CFLAGS += -I$(OPENSSLDIR)/include + OPENSSL_LINK = -L$(OPENSSLDIR)/lib $(CC_LD_DYNPATH)$(OPENSSLDIR)/lib else OPENSSL_LINK = endif else - ALL_CFLAGS += -DNO_OPENSSL + BASIC_CFLAGS += -DNO_OPENSSL MOZILLA_SHA1 = 1 OPENSSL_LIBSSL = endif @@@ -455,33 -482,30 +483,30 @@@ els endif ifdef NEEDS_LIBICONV ifdef ICONVDIR - # Again this may be problematic -- gcc does not always want -R. - ALL_CFLAGS += -I$(ICONVDIR)/include - ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib + BASIC_CFLAGS += -I$(ICONVDIR)/include + ICONV_LINK = -L$(ICONVDIR)/lib $(CC_LD_DYNPATH)$(ICONVDIR)/lib else ICONV_LINK = endif - LIBS += $(ICONV_LINK) -liconv + EXTLIBS += $(ICONV_LINK) -liconv endif ifdef NEEDS_SOCKET - LIBS += -lsocket - SIMPLE_LIB += -lsocket + EXTLIBS += -lsocket endif ifdef NEEDS_NSL - LIBS += -lnsl - SIMPLE_LIB += -lnsl + EXTLIBS += -lnsl endif ifdef NO_D_TYPE_IN_DIRENT - ALL_CFLAGS += -DNO_D_TYPE_IN_DIRENT + BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT endif ifdef NO_D_INO_IN_DIRENT - ALL_CFLAGS += -DNO_D_INO_IN_DIRENT + BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT endif ifdef NO_C99_FORMAT ALL_CFLAGS += -DNO_C99_FORMAT endif ifdef NO_SYMLINK_HEAD - ALL_CFLAGS += -DNO_SYMLINK_HEAD + BASIC_CFLAGS += -DNO_SYMLINK_HEAD endif ifdef NO_STRCASESTR COMPAT_CFLAGS += -DNO_STRCASESTR @@@ -495,7 -519,7 +520,7 @@@ ifdef NO_SETEN COMPAT_CFLAGS += -DNO_SETENV COMPAT_OBJS += compat/setenv.o endif - ifdef NO_SETENV + ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o endif @@@ -503,22 -527,35 +528,35 @@@ ifdef NO_MMA COMPAT_CFLAGS += -DNO_MMAP COMPAT_OBJS += compat/mmap.o endif + ifdef NO_PREAD + COMPAT_CFLAGS += -DNO_PREAD + COMPAT_OBJS += compat/pread.o + endif + ifdef NO_FAST_WORKING_DIRECTORY + BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY + endif + ifdef NO_TRUSTABLE_FILEMODE + BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE + endif ifdef NO_IPV6 - ALL_CFLAGS += -DNO_IPV6 + BASIC_CFLAGS += -DNO_IPV6 endif ifdef NO_SOCKADDR_STORAGE ifdef NO_IPV6 - ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in + BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in else - ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6 + BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6 endif endif ifdef NO_INET_NTOP LIB_OBJS += compat/inet_ntop.o endif + ifdef NO_INET_PTON + LIB_OBJS += compat/inet_pton.o + endif ifdef NO_ICONV - ALL_CFLAGS += -DNO_ICONV + BASIC_CFLAGS += -DNO_ICONV endif ifdef PPC_SHA1 @@@ -534,12 -571,12 +572,12 @@@ ifdef MOZILLA_SHA LIB_OBJS += mozilla-sha1/sha1.o else SHA1_HEADER = - LIBS += $(LIB_4_CRYPTO) + EXTLIBS += $(LIB_4_CRYPTO) endif endif endif - ifdef NO_ACCURATE_DIFF - ALL_CFLAGS += -DNO_ACCURATE_DIFF + ifdef NO_PERL_MAKEMAKER + export NO_PERL_MAKEMAKER endif # Shell quote (do not use $(call) to accommodate ancient setups); @@@ -554,17 -591,27 +592,27 @@@ prefix_SQ = $(subst ','\'',$(prefix) SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) - PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH)) - GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR)) - ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS) + LIBS = $(GITLIBS) $(EXTLIBS) + + BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS) LIB_OBJS += $(COMPAT_OBJS) + + ALL_CFLAGS += $(BASIC_CFLAGS) + ALL_LDFLAGS += $(BASIC_LDFLAGS) + export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir + + ### Build rules - all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi + all:: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi + ifneq (,$X) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$p';) + endif - all: + all:: + $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all $(MAKE) -C templates strip: $(PROGRAMS) git$X @@@ -577,6 -624,9 +625,9 @@@ git$X: git.c common-cmds.h $(BUILTIN_OB help.o: common-cmds.h + git-merge-recur$X: git-merge-recursive$X + rm -f $@ && ln git-merge-recursive$X $@ + $(BUILT_INS): git$X rm -f $@ && ln git$X $@ @@@ -590,25 -640,28 +641,28 @@@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : % -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ - -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \ $@.sh >$@+ chmod +x $@+ mv $@+ $@ - $(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl - rm -f $@ $@+ - sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ - -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ - $@.perl >$@+ - chmod +x $@+ - mv $@+ $@ + $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak + + perl/perl.mak: GIT-CFLAGS + $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F) - $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py GIT-CFLAGS + $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl rm -f $@ $@+ - sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \ - -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \ + INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \ + sed -e '1{' \ + -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ + -e ' h' \ + -e ' s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \ + -e ' H' \ + -e ' x' \ + -e '}' \ + -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ - $@.py >$@+ + $@.perl >$@+ chmod +x $@+ mv $@+ $@ @@@ -629,11 -682,16 +683,16 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ + -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \ + -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \ -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \ -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \ -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \ -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \ -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \ + -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \ + -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \ + -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \ $< >$@+ chmod +x $@+ mv $@+ $@ @@@ -643,7 -701,6 +702,6 @@@ git-instaweb: git-instaweb.sh gitweb/gi sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ - -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \ -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \ -e '/@@GITWEB_CGI@@/d' \ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ @@@ -663,7 -720,6 +721,6 @@@ configure: configure.a git$X git.spec \ $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ : GIT-VERSION-FILE %.o: %.c GIT-CFLAGS @@@ -687,11 -743,6 +744,6 @@@ endi git-%$X: %.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) - $(SIMPLE_PROGRAMS) : $(LIB_FILE) - $(SIMPLE_PROGRAMS) : git-%$X : %.o - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIB_FILE) $(SIMPLE_LIB) - ssh-pull.o: ssh-fetch.c ssh-push.o: ssh-upload.c git-local-fetch$X: fetch.o @@@ -718,12 -769,19 +770,19 @@@ $(DIFF_OBJS): diffcore. $(LIB_FILE): $(LIB_OBJS) rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) - XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o + XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ + xdiff/xmerge.o + $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ + xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h $(XDIFF_LIB): $(XDIFF_OBJS) rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) + perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS + (cd perl && $(PERL_PATH) Makefile.PL \ + PREFIX='$(prefix_SQ)') + doc: $(MAKE) -C Documentation all @@@ -736,7 -794,7 +795,7 @@@ tags find . -name '*.[hcS]' -print | xargs ctags -a ### Detect prefix changes - TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):$(GIT_PYTHON_DIR_SQ):\ + TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) GIT-CFLAGS: .FORCE-GIT-CFLAGS @@@ -752,7 -810,6 +811,6 @@@ # However, the environment gets quite big, and some programs have problems # with that. - export NO_PYTHON export NO_SVN_TESTS test: all @@@ -761,8 -818,8 +819,8 @@@ test-date$X: test-date.c date.o ctype.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o - test-delta$X: test-delta.c diff-delta.o patch-delta.o - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ + test-delta$X: test-delta.o diff-delta.o patch-delta.o $(GITLIBS) + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) @@@ -773,7 -830,7 +831,7 @@@ test-sha1$X: test-sha1.o $(GITLIBS check-sha1:: test-sha1$X ./test-sha1.sh - check: + check: common-cmds.h for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done @@@ -786,8 -843,7 +844,7 @@@ install: al $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)' - $(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)' + $(MAKE) -C perl prefix='$(prefix_SQ)' install if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \ then \ ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ @@@ -796,10 -852,15 +853,15 @@@ '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ fi $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) + ifneq (,$X) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) + endif install-doc: $(MAKE) -C Documentation install + quick-install-doc: + $(MAKE) -C Documentation quick-install @@@ -810,8 -871,9 +872,9 @@@ git.spec: git.spec.i mv $@+ $@ GIT_TARNAME=git-$(GIT_VERSION) - dist: git.spec git-tar-tree - ./git-tar-tree HEAD^{tree} $(GIT_TARNAME) > $(GIT_TARNAME).tar + dist: git.spec git-archive + ./git-archive --format=tar \ + --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar @mkdir -p $(GIT_TARNAME) @cp git.spec $(GIT_TARNAME) @echo $(GIT_VERSION) > $(GIT_TARNAME)/version @@@ -856,7 -918,8 +919,8 @@@ clean rm -f $(htmldocs).tar.gz $(manpages).tar.gz rm -f gitweb/gitweb.cgi $(MAKE) -C Documentation/ clean - $(MAKE) -C templates clean + $(MAKE) -C perl clean + $(MAKE) -C templates/ clean $(MAKE) -C t/ clean rm -f GIT-VERSION-FILE GIT-CFLAGS @@@ -870,7 -933,7 +934,7 @@@ check-docs: do \ case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ - git-merge-resolve | git-merge-stupid | \ + git-merge-resolve | git-merge-stupid | git-merge-recur | \ git-ssh-pull | git-ssh-push ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ @@@ -881,3 -944,8 +945,8 @@@ *) echo "no link: $$v";; \ esac ; \ done | sort + + ### Make sure built-ins do not have dups and listed in git.c + # + check-builtins:: + ./check-builtins.sh diff --combined fast-import.c index 38e24bf6a6,0000000000..492a8594bf mode 100644,000000..100644 --- a/fast-import.c +++ b/fast-import.c @@@ -1,1915 -1,0 +1,1915 @@@ +/* +Format of STDIN stream: + + stream ::= cmd*; + + cmd ::= new_blob + | new_commit + | new_tag + | reset_branch + ; + + new_blob ::= 'blob' lf + mark? + file_content; + file_content ::= data; + + new_commit ::= 'commit' sp ref_str lf + mark? + ('author' sp name '<' email '>' ts tz lf)? + 'committer' sp name '<' email '>' ts tz lf + commit_msg + ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)? + ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)* + file_change* + lf; + commit_msg ::= data; + + file_change ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf + | 'D' sp path_str lf + ; + mode ::= '644' | '755'; + + new_tag ::= 'tag' sp tag_str lf + 'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf + 'tagger' sp name '<' email '>' ts tz lf + tag_msg; + tag_msg ::= data; + + reset_branch ::= 'reset' sp ref_str lf + ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)? + lf; + + # note: the first idnum in a stream should be 1 and subsequent + # idnums should not have gaps between values as this will cause + # the stream parser to reserve space for the gapped values. An + # idnum can be updated in the future to a new object by issuing + # a new mark directive with the old idnum. + # + mark ::= 'mark' sp idnum lf; + + # note: declen indicates the length of binary_data in bytes. + # declen does not include the lf preceeding or trailing the + # binary data. + # + data ::= 'data' sp declen lf + binary_data + lf; + + # note: quoted strings are C-style quoting supporting \c for + # common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn + # is the signed byte value in octal. Note that the only + # characters which must actually be escaped to protect the + # stream formatting is: \, " and LF. Otherwise these values + # are UTF8. + # + ref_str ::= ref | '"' quoted(ref) '"' ; + sha1exp_str ::= sha1exp | '"' quoted(sha1exp) '"' ; + tag_str ::= tag | '"' quoted(tag) '"' ; + path_str ::= path | '"' quoted(path) '"' ; + + declen ::= # unsigned 32 bit value, ascii base10 notation; + binary_data ::= # file content, not interpreted; + + sp ::= # ASCII space character; + lf ::= # ASCII newline (LF) character; + + # note: a colon (':') must precede the numerical value assigned to + # an idnum. This is to distinguish it from a ref or tag name as + # GIT does not permit ':' in ref or tag strings. + # + idnum ::= ':' declen; + path ::= # GIT style file path, e.g. "a/b/c"; + ref ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT"; + tag ::= # GIT tag name, e.g. "FIREFOX_1_5"; + sha1exp ::= # Any valid GIT SHA1 expression; + hexsha1 ::= # SHA1 in hexadecimal format; + + # note: name and email are UTF8 strings, however name must not + # contain '<' or lf and email must not contain any of the + # following: '<', '>', lf. + # + name ::= # valid GIT author/committer name; + email ::= # valid GIT author/committer email; + ts ::= # time since the epoch in seconds, ascii base10 notation; + tz ::= # GIT style timezone; +*/ + +#include "builtin.h" +#include "cache.h" +#include "object.h" +#include "blob.h" +#include "tree.h" +#include "delta.h" +#include "pack.h" +#include "refs.h" +#include "csum-file.h" +#include "strbuf.h" +#include "quote.h" + +struct object_entry +{ + struct object_entry *next; + enum object_type type; + unsigned long offset; + unsigned char sha1[20]; +}; + +struct object_entry_pool +{ + struct object_entry_pool *next_pool; + struct object_entry *next_free; + struct object_entry *end; + struct object_entry entries[FLEX_ARRAY]; /* more */ +}; + +struct mark_set +{ + int shift; + union { + struct object_entry *marked[1024]; + struct mark_set *sets[1024]; + } data; +}; + +struct last_object +{ + void *data; + unsigned long len; + unsigned int depth; + int no_free; + unsigned char sha1[20]; +}; + +struct mem_pool +{ + struct mem_pool *next_pool; + char *next_free; + char *end; + char space[FLEX_ARRAY]; /* more */ +}; + +struct atom_str +{ + struct atom_str *next_atom; + int str_len; + char str_dat[FLEX_ARRAY]; /* more */ +}; + +struct tree_content; +struct tree_entry +{ + struct tree_content *tree; + struct atom_str* name; + struct tree_entry_ms + { + unsigned int mode; + unsigned char sha1[20]; + } versions[2]; +}; + +struct tree_content +{ + unsigned int entry_capacity; /* must match avail_tree_content */ + unsigned int entry_count; + unsigned int delta_depth; + struct tree_entry *entries[FLEX_ARRAY]; /* more */ +}; + +struct avail_tree_content +{ + unsigned int entry_capacity; /* must match tree_content */ + struct avail_tree_content *next_avail; +}; + +struct branch +{ + struct branch *table_next_branch; + struct branch *active_next_branch; + const char *name; + unsigned long last_commit; + struct tree_entry branch_tree; + unsigned char sha1[20]; +}; + +struct tag +{ + struct tag *next_tag; + const char *name; + unsigned char sha1[20]; +}; + +struct dbuf +{ + void *buffer; + size_t capacity; +}; + +struct hash_list +{ + struct hash_list *next; + unsigned char sha1[20]; +}; + +/* Stats and misc. counters */ +static unsigned long max_depth = 10; +static unsigned long alloc_count; +static unsigned long branch_count; +static unsigned long branch_load_count; +static unsigned long remap_count; +static unsigned long object_count; +static unsigned long duplicate_count; +static unsigned long marks_set_count; +static unsigned long object_count_by_type[9]; +static unsigned long duplicate_count_by_type[9]; +static unsigned long delta_count_by_type[9]; + +/* Memory pools */ +static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool); +static size_t total_allocd; +static struct mem_pool *mem_pool; + +/* Atom management */ +static unsigned int atom_table_sz = 4451; +static unsigned int atom_cnt; +static struct atom_str **atom_table; + +/* The .pack file being generated */ +static int pack_fd; +static unsigned long pack_size; +static unsigned char pack_sha1[20]; +static unsigned char* pack_base; +static unsigned long pack_moff; +static unsigned long pack_mlen = 128*1024*1024; +static unsigned long page_size; + +/* Table of objects we've written. */ +static unsigned int object_entry_alloc = 5000; +static struct object_entry_pool *blocks; +static struct object_entry *object_table[1 << 16]; +static struct mark_set *marks; +static const char* mark_file; + +/* Our last blob */ +static struct last_object last_blob; + +/* Tree management */ +static unsigned int tree_entry_alloc = 1000; +static void *avail_tree_entry; +static unsigned int avail_tree_table_sz = 100; +static struct avail_tree_content **avail_tree_table; +static struct dbuf old_tree; +static struct dbuf new_tree; + +/* Branch data */ +static unsigned long max_active_branches = 5; +static unsigned long cur_active_branches; +static unsigned long branch_table_sz = 1039; +static struct branch **branch_table; +static struct branch *active_branches; + +/* Tag data */ +static struct tag *first_tag; +static struct tag *last_tag; + +/* Input stream parsing */ +static struct strbuf command_buf; +static unsigned long next_mark; +static struct dbuf new_data; +static FILE* branch_log; + + +static void alloc_objects(int cnt) +{ + struct object_entry_pool *b; + + b = xmalloc(sizeof(struct object_entry_pool) + + cnt * sizeof(struct object_entry)); + b->next_pool = blocks; + b->next_free = b->entries; + b->end = b->entries + cnt; + blocks = b; + alloc_count += cnt; +} + +static struct object_entry* new_object(unsigned char *sha1) +{ + struct object_entry *e; + + if (blocks->next_free == blocks->end) + alloc_objects(object_entry_alloc); + + e = blocks->next_free++; + hashcpy(e->sha1, sha1); + return e; +} + +static struct object_entry* find_object(unsigned char *sha1) +{ + unsigned int h = sha1[0] << 8 | sha1[1]; + struct object_entry *e; + for (e = object_table[h]; e; e = e->next) + if (!hashcmp(sha1, e->sha1)) + return e; + return NULL; +} + +static struct object_entry* insert_object(unsigned char *sha1) +{ + unsigned int h = sha1[0] << 8 | sha1[1]; + struct object_entry *e = object_table[h]; + struct object_entry *p = NULL; + + while (e) { + if (!hashcmp(sha1, e->sha1)) + return e; + p = e; + e = e->next; + } + + e = new_object(sha1); + e->next = NULL; + e->offset = 0; + if (p) + p->next = e; + else + object_table[h] = e; + return e; +} + +static unsigned int hc_str(const char *s, size_t len) +{ + unsigned int r = 0; + while (len-- > 0) + r = r * 31 + *s++; + return r; +} + +static void* pool_alloc(size_t len) +{ + struct mem_pool *p; + void *r; + + for (p = mem_pool; p; p = p->next_pool) + if ((p->end - p->next_free >= len)) + break; + + if (!p) { + if (len >= (mem_pool_alloc/2)) { + total_allocd += len; + return xmalloc(len); + } + total_allocd += sizeof(struct mem_pool) + mem_pool_alloc; + p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc); + p->next_pool = mem_pool; + p->next_free = p->space; + p->end = p->next_free + mem_pool_alloc; + mem_pool = p; + } + + r = p->next_free; + /* round out to a pointer alignment */ + if (len & (sizeof(void*) - 1)) + len += sizeof(void*) - (len & (sizeof(void*) - 1)); + p->next_free += len; + return r; +} + +static void* pool_calloc(size_t count, size_t size) +{ + size_t len = count * size; + void *r = pool_alloc(len); + memset(r, 0, len); + return r; +} + +static char* pool_strdup(const char *s) +{ + char *r = pool_alloc(strlen(s) + 1); + strcpy(r, s); + return r; +} + +static void size_dbuf(struct dbuf *b, size_t maxlen) +{ + if (b->buffer) { + if (b->capacity >= maxlen) + return; + free(b->buffer); + } + b->capacity = ((maxlen / 1024) + 1) * 1024; + b->buffer = xmalloc(b->capacity); +} + +static void insert_mark(unsigned long idnum, struct object_entry *oe) +{ + struct mark_set *s = marks; + while ((idnum >> s->shift) >= 1024) { + s = pool_calloc(1, sizeof(struct mark_set)); + s->shift = marks->shift + 10; + s->data.sets[0] = marks; + marks = s; + } + while (s->shift) { + unsigned long i = idnum >> s->shift; + idnum -= i << s->shift; + if (!s->data.sets[i]) { + s->data.sets[i] = pool_calloc(1, sizeof(struct mark_set)); + s->data.sets[i]->shift = s->shift - 10; + } + s = s->data.sets[i]; + } + if (!s->data.marked[idnum]) + marks_set_count++; + s->data.marked[idnum] = oe; +} + +static struct object_entry* find_mark(unsigned long idnum) +{ + unsigned long orig_idnum = idnum; + struct mark_set *s = marks; + struct object_entry *oe = NULL; + if ((idnum >> s->shift) < 1024) { + while (s && s->shift) { + unsigned long i = idnum >> s->shift; + idnum -= i << s->shift; + s = s->data.sets[i]; + } + if (s) + oe = s->data.marked[idnum]; + } + if (!oe) + die("mark :%lu not declared", orig_idnum); + return oe; +} + +static struct atom_str* to_atom(const char *s, size_t len) +{ + unsigned int hc = hc_str(s, len) % atom_table_sz; + struct atom_str *c; + + for (c = atom_table[hc]; c; c = c->next_atom) + if (c->str_len == len && !strncmp(s, c->str_dat, len)) + return c; + + c = pool_alloc(sizeof(struct atom_str) + len + 1); + c->str_len = len; + strncpy(c->str_dat, s, len); + c->str_dat[len] = 0; + c->next_atom = atom_table[hc]; + atom_table[hc] = c; + atom_cnt++; + return c; +} + +static struct branch* lookup_branch(const char *name) +{ + unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; + struct branch *b; + + for (b = branch_table[hc]; b; b = b->table_next_branch) + if (!strcmp(name, b->name)) + return b; + return NULL; +} + +static struct branch* new_branch(const char *name) +{ + unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; + struct branch* b = lookup_branch(name); + + if (b) + die("Invalid attempt to create duplicate branch: %s", name); + if (check_ref_format(name)) + die("Branch name doesn't conform to GIT standards: %s", name); + + b = pool_calloc(1, sizeof(struct branch)); + b->name = pool_strdup(name); + b->table_next_branch = branch_table[hc]; + b->branch_tree.versions[0].mode = S_IFDIR; + b->branch_tree.versions[1].mode = S_IFDIR; + branch_table[hc] = b; + branch_count++; + return b; +} + +static unsigned int hc_entries(unsigned int cnt) +{ + cnt = cnt & 7 ? (cnt / 8) + 1 : cnt / 8; + return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1; +} + +static struct tree_content* new_tree_content(unsigned int cnt) +{ + struct avail_tree_content *f, *l = NULL; + struct tree_content *t; + unsigned int hc = hc_entries(cnt); + + for (f = avail_tree_table[hc]; f; l = f, f = f->next_avail) + if (f->entry_capacity >= cnt) + break; + + if (f) { + if (l) + l->next_avail = f->next_avail; + else + avail_tree_table[hc] = f->next_avail; + } else { + cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt; + f = pool_alloc(sizeof(*t) + sizeof(t->entries[0]) * cnt); + f->entry_capacity = cnt; + } + + t = (struct tree_content*)f; + t->entry_count = 0; + t->delta_depth = 0; + return t; +} + +static void release_tree_entry(struct tree_entry *e); +static void release_tree_content(struct tree_content *t) +{ + struct avail_tree_content *f = (struct avail_tree_content*)t; + unsigned int hc = hc_entries(f->entry_capacity); + f->next_avail = avail_tree_table[hc]; + avail_tree_table[hc] = f; +} + +static void release_tree_content_recursive(struct tree_content *t) +{ + unsigned int i; + for (i = 0; i < t->entry_count; i++) + release_tree_entry(t->entries[i]); + release_tree_content(t); +} + +static struct tree_content* grow_tree_content( + struct tree_content *t, + int amt) +{ + struct tree_content *r = new_tree_content(t->entry_count + amt); + r->entry_count = t->entry_count; + r->delta_depth = t->delta_depth; + memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0])); + release_tree_content(t); + return r; +} + +static struct tree_entry* new_tree_entry() +{ + struct tree_entry *e; + + if (!avail_tree_entry) { + unsigned int n = tree_entry_alloc; + total_allocd += n * sizeof(struct tree_entry); + avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry)); + while (n-- > 1) { + *((void**)e) = e + 1; + e++; + } + *((void**)e) = NULL; + } + + e = avail_tree_entry; + avail_tree_entry = *((void**)e); + return e; +} + +static void release_tree_entry(struct tree_entry *e) +{ + if (e->tree) + release_tree_content_recursive(e->tree); + *((void**)e) = avail_tree_entry; + avail_tree_entry = e; +} + +static void yread(int fd, void *buffer, size_t length) +{ + ssize_t ret = 0; + while (ret < length) { + ssize_t size = xread(fd, (char *) buffer + ret, length - ret); + if (!size) + die("Read from descriptor %i: end of stream", fd); + if (size < 0) + die("Read from descriptor %i: %s", fd, strerror(errno)); + ret += size; + } +} + +static size_t encode_header( + enum object_type type, + size_t size, + unsigned char *hdr) +{ + int n = 1; + unsigned char c; + - if (type < OBJ_COMMIT || type > OBJ_DELTA) ++ if (type < OBJ_COMMIT || type > OBJ_REF_DELTA) + die("bad type %d", type); + + c = (type << 4) | (size & 15); + size >>= 4; + while (size) { + *hdr++ = c | 0x80; + c = size & 0x7f; + size >>= 7; + n++; + } + *hdr = c; + return n; +} + +static int store_object( + enum object_type type, + void *dat, + size_t datlen, + struct last_object *last, + unsigned char *sha1out, + unsigned long mark) +{ + void *out, *delta; + struct object_entry *e; + unsigned char hdr[96]; + unsigned char sha1[20]; + unsigned long hdrlen, deltalen; + SHA_CTX c; + z_stream s; + + hdrlen = sprintf((char*)hdr,"%s %lu",type_names[type],datlen) + 1; + SHA1_Init(&c); + SHA1_Update(&c, hdr, hdrlen); + SHA1_Update(&c, dat, datlen); + SHA1_Final(sha1, &c); + if (sha1out) + hashcpy(sha1out, sha1); + + e = insert_object(sha1); + if (mark) + insert_mark(mark, e); + if (e->offset) { + duplicate_count++; + duplicate_count_by_type[type]++; + return 1; + } + e->type = type; + e->offset = pack_size; + object_count++; + object_count_by_type[type]++; + + if (last && last->data && last->depth < max_depth) + delta = diff_delta(last->data, last->len, + dat, datlen, + &deltalen, 0); + else + delta = 0; + + memset(&s, 0, sizeof(s)); + deflateInit(&s, zlib_compression_level); + + if (delta) { + delta_count_by_type[type]++; + last->depth++; + s.next_in = delta; + s.avail_in = deltalen; - hdrlen = encode_header(OBJ_DELTA, deltalen, hdr); ++ hdrlen = encode_header(OBJ_REF_DELTA, deltalen, hdr); + write_or_die(pack_fd, hdr, hdrlen); + write_or_die(pack_fd, last->sha1, sizeof(sha1)); + pack_size += hdrlen + sizeof(sha1); + } else { + if (last) + last->depth = 0; + s.next_in = dat; + s.avail_in = datlen; + hdrlen = encode_header(type, datlen, hdr); + write_or_die(pack_fd, hdr, hdrlen); + pack_size += hdrlen; + } + + s.avail_out = deflateBound(&s, s.avail_in); + s.next_out = out = xmalloc(s.avail_out); + while (deflate(&s, Z_FINISH) == Z_OK) + /* nothing */; + deflateEnd(&s); + + write_or_die(pack_fd, out, s.total_out); + pack_size += s.total_out; + + free(out); + if (delta) + free(delta); + if (last) { + if (last->data && !last->no_free) + free(last->data); + last->data = dat; + last->len = datlen; + hashcpy(last->sha1, sha1); + } + return 0; +} + +static unsigned char* map_pack(unsigned long offset, unsigned int *left) +{ + if (offset >= pack_size) + die("object offset outside of pack file"); + if (!pack_base + || offset < pack_moff + || (offset + 20) >= (pack_moff + pack_mlen)) { + if (pack_base) + munmap(pack_base, pack_mlen); + pack_moff = (offset / page_size) * page_size; + pack_base = mmap(NULL,pack_mlen,PROT_READ,MAP_SHARED, + pack_fd,pack_moff); + if (pack_base == MAP_FAILED) + die("Failed to map generated pack: %s", strerror(errno)); + remap_count++; + } + offset -= pack_moff; + if (left) + *left = pack_mlen - offset; + return pack_base + offset; +} + +static unsigned long unpack_object_header(unsigned long offset, + enum object_type *type, + unsigned long *sizep) +{ + unsigned shift; + unsigned char c; + unsigned long size; + + c = *map_pack(offset++, NULL); + *type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while (c & 0x80) { + c = *map_pack(offset++, NULL); + size += (c & 0x7f) << shift; + shift += 7; + } + *sizep = size; + return offset; +} + +static void *unpack_non_delta_entry(unsigned long o, unsigned long sz) +{ + z_stream stream; + unsigned char *result; + + result = xmalloc(sz + 1); + result[sz] = 0; + + memset(&stream, 0, sizeof(stream)); + stream.next_in = map_pack(o, &stream.avail_in); + stream.next_out = result; + stream.avail_out = sz; + + inflateInit(&stream); + for (;;) { + int st = inflate(&stream, Z_FINISH); + if (st == Z_STREAM_END) + break; + if (st == Z_OK || st == Z_BUF_ERROR) { + o = stream.next_in - pack_base + pack_moff; + stream.next_in = map_pack(o, &stream.avail_in); + continue; + } + die("Error %i from zlib during inflate.", st); + } + inflateEnd(&stream); + if (stream.total_out != sz) + die("Error after inflate: sizes mismatch"); + return result; +} + - static void *unpack_entry(unsigned long offset, ++static void *gfi_unpack_entry(unsigned long offset, + unsigned long *sizep, + unsigned int *delta_depth); + +static void *unpack_delta_entry(unsigned long offset, + unsigned long delta_size, + unsigned long *sizep, + unsigned int *delta_depth) +{ + struct object_entry *base_oe; + unsigned char *base_sha1; + void *delta_data, *base, *result; + unsigned long base_size, result_size; + + base_sha1 = map_pack(offset, NULL); + base_oe = find_object(base_sha1); + if (!base_oe) + die("I'm broken; I can't find a base I know must be here."); - base = unpack_entry(base_oe->offset, &base_size, delta_depth); ++ base = gfi_unpack_entry(base_oe->offset, &base_size, delta_depth); + delta_data = unpack_non_delta_entry(offset + 20, delta_size); + result = patch_delta(base, base_size, + delta_data, delta_size, + &result_size); + if (!result) + die("failed to apply delta"); + free(delta_data); + free(base); + *sizep = result_size; + (*delta_depth)++; + return result; +} + - static void *unpack_entry(unsigned long offset, ++static void *gfi_unpack_entry(unsigned long offset, + unsigned long *sizep, + unsigned int *delta_depth) +{ + unsigned long size; + enum object_type kind; + + offset = unpack_object_header(offset, &kind, &size); + switch (kind) { - case OBJ_DELTA: ++ case OBJ_REF_DELTA: + return unpack_delta_entry(offset, size, sizep, delta_depth); + case OBJ_COMMIT: + case OBJ_TREE: + case OBJ_BLOB: + case OBJ_TAG: + *sizep = size; + *delta_depth = 0; + return unpack_non_delta_entry(offset, size); + default: + die("I created an object I can't read!"); + } +} + +static const char *get_mode(const char *str, unsigned int *modep) +{ + unsigned char c; + unsigned int mode = 0; + + while ((c = *str++) != ' ') { + if (c < '0' || c > '7') + return NULL; + mode = (mode << 3) + (c - '0'); + } + *modep = mode; + return str; +} + +static void load_tree(struct tree_entry *root) +{ + unsigned char* sha1 = root->versions[1].sha1; + struct object_entry *myoe; + struct tree_content *t; + unsigned long size; + char *buf; + const char *c; + + root->tree = t = new_tree_content(8); + if (is_null_sha1(sha1)) + return; + + myoe = find_object(sha1); + if (myoe) { + if (myoe->type != OBJ_TREE) + die("Not a tree: %s", sha1_to_hex(sha1)); - buf = unpack_entry(myoe->offset, &size, &t->delta_depth); ++ buf = gfi_unpack_entry(myoe->offset, &size, &t->delta_depth); + } else { + char type[20]; + buf = read_sha1_file(sha1, type, &size); + if (!buf || strcmp(type, tree_type)) + die("Can't load tree %s", sha1_to_hex(sha1)); + } + + c = buf; + while (c != (buf + size)) { + struct tree_entry *e = new_tree_entry(); + + if (t->entry_count == t->entry_capacity) + root->tree = t = grow_tree_content(t, 8); + t->entries[t->entry_count++] = e; + + e->tree = NULL; + c = get_mode(c, &e->versions[1].mode); + if (!c) + die("Corrupt mode in %s", sha1_to_hex(sha1)); + e->versions[0].mode = e->versions[1].mode; + e->name = to_atom(c, strlen(c)); + c += e->name->str_len + 1; + hashcpy(e->versions[0].sha1, (unsigned char*)c); + hashcpy(e->versions[1].sha1, (unsigned char*)c); + c += 20; + } + free(buf); +} + +static int tecmp0 (const void *_a, const void *_b) +{ + struct tree_entry *a = *((struct tree_entry**)_a); + struct tree_entry *b = *((struct tree_entry**)_b); + return base_name_compare( + a->name->str_dat, a->name->str_len, a->versions[0].mode, + b->name->str_dat, b->name->str_len, b->versions[0].mode); +} + +static int tecmp1 (const void *_a, const void *_b) +{ + struct tree_entry *a = *((struct tree_entry**)_a); + struct tree_entry *b = *((struct tree_entry**)_b); + return base_name_compare( + a->name->str_dat, a->name->str_len, a->versions[1].mode, + b->name->str_dat, b->name->str_len, b->versions[1].mode); +} + +static void mktree(struct tree_content *t, + int v, + unsigned long *szp, + struct dbuf *b) +{ + size_t maxlen = 0; + unsigned int i; + char *c; + + if (!v) + qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0); + else + qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp1); + + for (i = 0; i < t->entry_count; i++) { + if (t->entries[i]->versions[v].mode) + maxlen += t->entries[i]->name->str_len + 34; + } + + size_dbuf(b, maxlen); + c = b->buffer; + for (i = 0; i < t->entry_count; i++) { + struct tree_entry *e = t->entries[i]; + if (!e->versions[v].mode) + continue; + c += sprintf(c, "%o", e->versions[v].mode); + *c++ = ' '; + strcpy(c, e->name->str_dat); + c += e->name->str_len + 1; + hashcpy((unsigned char*)c, e->versions[v].sha1); + c += 20; + } + *szp = c - (char*)b->buffer; +} + +static void store_tree(struct tree_entry *root) +{ + struct tree_content *t = root->tree; + unsigned int i, j, del; + unsigned long new_len; + struct last_object lo; + + if (!is_null_sha1(root->versions[1].sha1)) + return; + + for (i = 0; i < t->entry_count; i++) { + if (t->entries[i]->tree) + store_tree(t->entries[i]); + } + + if (!S_ISDIR(root->versions[0].mode) + || is_null_sha1(root->versions[0].sha1) + || !find_object(root->versions[0].sha1)) { + lo.data = NULL; + lo.depth = 0; + } else { + mktree(t, 0, &lo.len, &old_tree); + lo.data = old_tree.buffer; + lo.depth = t->delta_depth; + lo.no_free = 1; + hashcpy(lo.sha1, root->versions[0].sha1); + } + + mktree(t, 1, &new_len, &new_tree); + store_object(OBJ_TREE, new_tree.buffer, new_len, + &lo, root->versions[1].sha1, 0); + + t->delta_depth = lo.depth; + for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { + struct tree_entry *e = t->entries[i]; + if (e->versions[1].mode) { + e->versions[0].mode = e->versions[1].mode; + hashcpy(e->versions[0].sha1, e->versions[1].sha1); + t->entries[j++] = e; + } else { + release_tree_entry(e); + del++; + } + } + t->entry_count -= del; +} + +static int tree_content_set( + struct tree_entry *root, + const char *p, + const unsigned char *sha1, + const unsigned int mode) +{ + struct tree_content *t = root->tree; + const char *slash1; + unsigned int i, n; + struct tree_entry *e; + + slash1 = strchr(p, '/'); + if (slash1) + n = slash1 - p; + else + n = strlen(p); + + for (i = 0; i < t->entry_count; i++) { + e = t->entries[i]; + if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) { + if (!slash1) { + if (e->versions[1].mode == mode + && !hashcmp(e->versions[1].sha1, sha1)) + return 0; + e->versions[1].mode = mode; + hashcpy(e->versions[1].sha1, sha1); + if (e->tree) { + release_tree_content_recursive(e->tree); + e->tree = NULL; + } + hashclr(root->versions[1].sha1); + return 1; + } + if (!S_ISDIR(e->versions[1].mode)) { + e->tree = new_tree_content(8); + e->versions[1].mode = S_IFDIR; + } + if (!e->tree) + load_tree(e); + if (tree_content_set(e, slash1 + 1, sha1, mode)) { + hashclr(root->versions[1].sha1); + return 1; + } + return 0; + } + } + + if (t->entry_count == t->entry_capacity) + root->tree = t = grow_tree_content(t, 8); + e = new_tree_entry(); + e->name = to_atom(p, n); + e->versions[0].mode = 0; + hashclr(e->versions[0].sha1); + t->entries[t->entry_count++] = e; + if (slash1) { + e->tree = new_tree_content(8); + e->versions[1].mode = S_IFDIR; + tree_content_set(e, slash1 + 1, sha1, mode); + } else { + e->tree = NULL; + e->versions[1].mode = mode; + hashcpy(e->versions[1].sha1, sha1); + } + hashclr(root->versions[1].sha1); + return 1; +} + +static int tree_content_remove(struct tree_entry *root, const char *p) +{ + struct tree_content *t = root->tree; + const char *slash1; + unsigned int i, n; + struct tree_entry *e; + + slash1 = strchr(p, '/'); + if (slash1) + n = slash1 - p; + else + n = strlen(p); + + for (i = 0; i < t->entry_count; i++) { + e = t->entries[i]; + if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) { + if (!slash1 || !S_ISDIR(e->versions[1].mode)) + goto del_entry; + if (!e->tree) + load_tree(e); + if (tree_content_remove(e, slash1 + 1)) { + for (n = 0; n < e->tree->entry_count; n++) { + if (e->tree->entries[n]->versions[1].mode) { + hashclr(root->versions[1].sha1); + return 1; + } + } + goto del_entry; + } + return 0; + } + } + return 0; + +del_entry: + if (e->tree) { + release_tree_content_recursive(e->tree); + e->tree = NULL; + } + e->versions[1].mode = 0; + hashclr(e->versions[1].sha1); + hashclr(root->versions[1].sha1); + return 1; +} + +static void init_pack_header() +{ + struct pack_header hdr; + + hdr.hdr_signature = htonl(PACK_SIGNATURE); + hdr.hdr_version = htonl(2); + hdr.hdr_entries = 0; + + write_or_die(pack_fd, &hdr, sizeof(hdr)); + pack_size = sizeof(hdr); +} + +static void fixup_header_footer() +{ + SHA_CTX c; + char hdr[8]; + unsigned long cnt; + char *buf; + size_t n; + + if (lseek(pack_fd, 0, SEEK_SET) != 0) + die("Failed seeking to start: %s", strerror(errno)); + + SHA1_Init(&c); + yread(pack_fd, hdr, 8); + SHA1_Update(&c, hdr, 8); + + cnt = htonl(object_count); + SHA1_Update(&c, &cnt, 4); + write_or_die(pack_fd, &cnt, 4); + + buf = xmalloc(128 * 1024); + for (;;) { + n = xread(pack_fd, buf, 128 * 1024); + if (n <= 0) + break; + SHA1_Update(&c, buf, n); + } + free(buf); + + SHA1_Final(pack_sha1, &c); + write_or_die(pack_fd, pack_sha1, sizeof(pack_sha1)); +} + +static int oecmp (const void *_a, const void *_b) +{ + struct object_entry *a = *((struct object_entry**)_a); + struct object_entry *b = *((struct object_entry**)_b); + return hashcmp(a->sha1, b->sha1); +} + +static void write_index(const char *idx_name) +{ + struct sha1file *f; + struct object_entry **idx, **c, **last; + struct object_entry *e; + struct object_entry_pool *o; + unsigned int array[256]; + int i; + + /* Build the sorted table of object IDs. */ + idx = xmalloc(object_count * sizeof(struct object_entry*)); + c = idx; + for (o = blocks; o; o = o->next_pool) + for (e = o->entries; e != o->next_free; e++) + *c++ = e; + last = idx + object_count; + qsort(idx, object_count, sizeof(struct object_entry*), oecmp); + + /* Generate the fan-out array. */ + c = idx; + for (i = 0; i < 256; i++) { + struct object_entry **next = c;; + while (next < last) { + if ((*next)->sha1[0] != i) + break; + next++; + } + array[i] = htonl(next - idx); + c = next; + } + + f = sha1create("%s", idx_name); + sha1write(f, array, 256 * sizeof(int)); + for (c = idx; c != last; c++) { + unsigned int offset = htonl((*c)->offset); + sha1write(f, &offset, 4); + sha1write(f, (*c)->sha1, sizeof((*c)->sha1)); + } + sha1write(f, pack_sha1, sizeof(pack_sha1)); + sha1close(f, NULL, 1); + free(idx); +} + +static void dump_branches() +{ + static const char *msg = "fast-import"; + unsigned int i; + struct branch *b; + struct ref_lock *lock; + + for (i = 0; i < branch_table_sz; i++) { + for (b = branch_table[i]; b; b = b->table_next_branch) { - lock = lock_any_ref_for_update(b->name, NULL, 0); ++ lock = lock_any_ref_for_update(b->name, NULL); + if (!lock || write_ref_sha1(lock, b->sha1, msg) < 0) + die("Can't write %s", b->name); + } + } +} + +static void dump_tags() +{ + static const char *msg = "fast-import"; + struct tag *t; + struct ref_lock *lock; + char path[PATH_MAX]; + + for (t = first_tag; t; t = t->next_tag) { + sprintf(path, "refs/tags/%s", t->name); - lock = lock_any_ref_for_update(path, NULL, 0); ++ lock = lock_any_ref_for_update(path, NULL); + if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0) + die("Can't write %s", path); + } +} + +static void dump_marks_helper(FILE *f, + unsigned long base, + struct mark_set *m) +{ + int k; + if (m->shift) { + for (k = 0; k < 1024; k++) { + if (m->data.sets[k]) + dump_marks_helper(f, (base + k) << m->shift, + m->data.sets[k]); + } + } else { + for (k = 0; k < 1024; k++) { + if (m->data.marked[k]) + fprintf(f, ":%lu %s\n", base + k, + sha1_to_hex(m->data.marked[k]->sha1)); + } + } +} + +static void dump_marks() +{ + if (mark_file) + { + FILE *f = fopen(mark_file, "w"); + dump_marks_helper(f, 0, marks); + fclose(f); + } +} + +static void read_next_command() +{ + read_line(&command_buf, stdin, '\n'); +} + +static void cmd_mark() +{ + if (!strncmp("mark :", command_buf.buf, 6)) { + next_mark = strtoul(command_buf.buf + 6, NULL, 10); + read_next_command(); + } + else + next_mark = 0; +} + +static void* cmd_data (size_t *size) +{ + size_t n = 0; + void *buffer; + size_t length; + + if (strncmp("data ", command_buf.buf, 5)) + die("Expected 'data n' command, found: %s", command_buf.buf); + + length = strtoul(command_buf.buf + 5, NULL, 10); + buffer = xmalloc(length); + + while (n < length) { + size_t s = fread((char*)buffer + n, 1, length - n, stdin); + if (!s && feof(stdin)) + die("EOF in data (%lu bytes remaining)", length - n); + n += s; + } + + if (fgetc(stdin) != '\n') + die("An lf did not trail the binary data as expected."); + + *size = length; + return buffer; +} + +static void cmd_new_blob() +{ + size_t l; + void *d; + + read_next_command(); + cmd_mark(); + d = cmd_data(&l); + + if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark)) + free(d); +} + +static void unload_one_branch() +{ + while (cur_active_branches + && cur_active_branches >= max_active_branches) { + unsigned long min_commit = ULONG_MAX; + struct branch *e, *l = NULL, *p = NULL; + + for (e = active_branches; e; e = e->active_next_branch) { + if (e->last_commit < min_commit) { + p = l; + min_commit = e->last_commit; + } + l = e; + } + + if (p) { + e = p->active_next_branch; + p->active_next_branch = e->active_next_branch; + } else { + e = active_branches; + active_branches = e->active_next_branch; + } + e->active_next_branch = NULL; + if (e->branch_tree.tree) { + release_tree_content_recursive(e->branch_tree.tree); + e->branch_tree.tree = NULL; + } + cur_active_branches--; + } +} + +static void load_branch(struct branch *b) +{ + load_tree(&b->branch_tree); + b->active_next_branch = active_branches; + active_branches = b; + cur_active_branches++; + branch_load_count++; +} + +static void file_change_m(struct branch *b) +{ + const char *p = command_buf.buf + 2; + char *p_uq; + const char *endp; + struct object_entry *oe; + unsigned char sha1[20]; + unsigned int mode; + char type[20]; + + p = get_mode(p, &mode); + if (!p) + die("Corrupt mode: %s", command_buf.buf); + switch (mode) { + case S_IFREG | 0644: + case S_IFREG | 0755: + case S_IFLNK: + case 0644: + case 0755: + /* ok */ + break; + default: + die("Corrupt mode: %s", command_buf.buf); + } + + if (*p == ':') { + char *x; + oe = find_mark(strtoul(p + 1, &x, 10)); + hashcpy(sha1, oe->sha1); + p = x; + } else { + if (get_sha1_hex(p, sha1)) + die("Invalid SHA1: %s", command_buf.buf); + oe = find_object(sha1); + p += 40; + } + if (*p++ != ' ') + die("Missing space after SHA1: %s", command_buf.buf); + + p_uq = unquote_c_style(p, &endp); + if (p_uq) { + if (*endp) + die("Garbage after path in: %s", command_buf.buf); + p = p_uq; + } + + if (oe) { + if (oe->type != OBJ_BLOB) + die("Not a blob (actually a %s): %s", + command_buf.buf, type_names[oe->type]); + } else { + if (sha1_object_info(sha1, type, NULL)) + die("Blob not found: %s", command_buf.buf); + if (strcmp(blob_type, type)) + die("Not a blob (actually a %s): %s", + command_buf.buf, type); + } + + tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode); + + if (p_uq) + free(p_uq); +} + +static void file_change_d(struct branch *b) +{ + const char *p = command_buf.buf + 2; + char *p_uq; + const char *endp; + + p_uq = unquote_c_style(p, &endp); + if (p_uq) { + if (*endp) + die("Garbage after path in: %s", command_buf.buf); + p = p_uq; + } + tree_content_remove(&b->branch_tree, p); + if (p_uq) + free(p_uq); +} + +static void cmd_from(struct branch *b) +{ + const char *from, *endp; + char *str_uq; + struct branch *s; + + if (strncmp("from ", command_buf.buf, 5)) + return; + + if (b->last_commit) + die("Can't reinitailize branch %s", b->name); + + from = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(from, &endp); + if (str_uq) { + if (*endp) + die("Garbage after string in: %s", command_buf.buf); + from = str_uq; + } + + s = lookup_branch(from); + if (b == s) + die("Can't create a branch from itself: %s", b->name); + else if (s) { + unsigned char *t = s->branch_tree.versions[1].sha1; + hashcpy(b->sha1, s->sha1); + hashcpy(b->branch_tree.versions[0].sha1, t); + hashcpy(b->branch_tree.versions[1].sha1, t); + } else if (*from == ':') { + unsigned long idnum = strtoul(from + 1, NULL, 10); + struct object_entry *oe = find_mark(idnum); + unsigned long size; + unsigned int depth; + char *buf; + if (oe->type != OBJ_COMMIT) + die("Mark :%lu not a commit", idnum); + hashcpy(b->sha1, oe->sha1); - buf = unpack_entry(oe->offset, &size, &depth); ++ buf = gfi_unpack_entry(oe->offset, &size, &depth); + if (!buf || size < 46) + die("Not a valid commit: %s", from); + if (memcmp("tree ", buf, 5) + || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) + die("The commit %s is corrupt", sha1_to_hex(b->sha1)); + free(buf); + hashcpy(b->branch_tree.versions[0].sha1, + b->branch_tree.versions[1].sha1); + } else if (!get_sha1(from, b->sha1)) { + if (is_null_sha1(b->sha1)) { + hashclr(b->branch_tree.versions[0].sha1); + hashclr(b->branch_tree.versions[1].sha1); + } else { + unsigned long size; + char *buf; + + buf = read_object_with_reference(b->sha1, + type_names[OBJ_COMMIT], &size, b->sha1); + if (!buf || size < 46) + die("Not a valid commit: %s", from); + if (memcmp("tree ", buf, 5) + || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) + die("The commit %s is corrupt", sha1_to_hex(b->sha1)); + free(buf); + hashcpy(b->branch_tree.versions[0].sha1, + b->branch_tree.versions[1].sha1); + } + } else + die("Invalid ref name or SHA1 expression: %s", from); + + read_next_command(); +} + +static struct hash_list* cmd_merge(unsigned int *count) +{ + struct hash_list *list = NULL, *n, *e; + const char *from, *endp; + char *str_uq; + struct branch *s; + + *count = 0; + while (!strncmp("merge ", command_buf.buf, 6)) { + from = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(from, &endp); + if (str_uq) { + if (*endp) + die("Garbage after string in: %s", command_buf.buf); + from = str_uq; + } + + n = xmalloc(sizeof(*n)); + s = lookup_branch(from); + if (s) + hashcpy(n->sha1, s->sha1); + else if (*from == ':') { + unsigned long idnum = strtoul(from + 1, NULL, 10); + struct object_entry *oe = find_mark(idnum); + if (oe->type != OBJ_COMMIT) + die("Mark :%lu not a commit", idnum); + hashcpy(n->sha1, oe->sha1); + } else if (get_sha1(from, n->sha1)) + die("Invalid ref name or SHA1 expression: %s", from); + + n->next = NULL; + if (list) + e->next = n; + else + list = n; + e = n; + *count++; + read_next_command(); + } + return list; +} + +static void cmd_new_commit() +{ + struct branch *b; + void *msg; + size_t msglen; + char *str_uq; + const char *endp; + char *sp; + char *author = NULL; + char *committer = NULL; + struct hash_list *merge_list = NULL; + unsigned int merge_count; + + /* Obtain the branch name from the rest of our command */ + sp = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(sp, &endp); + if (str_uq) { + if (*endp) + die("Garbage after ref in: %s", command_buf.buf); + sp = str_uq; + } + b = lookup_branch(sp); + if (!b) + b = new_branch(sp); + if (str_uq) + free(str_uq); + + read_next_command(); + cmd_mark(); + if (!strncmp("author ", command_buf.buf, 7)) { + author = strdup(command_buf.buf); + read_next_command(); + } + if (!strncmp("committer ", command_buf.buf, 10)) { + committer = strdup(command_buf.buf); + read_next_command(); + } + if (!committer) + die("Expected committer but didn't get one"); + msg = cmd_data(&msglen); + read_next_command(); + cmd_from(b); + merge_list = cmd_merge(&merge_count); + + /* ensure the branch is active/loaded */ + if (!b->branch_tree.tree || !max_active_branches) { + unload_one_branch(); + load_branch(b); + } + + /* file_change* */ + for (;;) { + if (1 == command_buf.len) + break; + else if (!strncmp("M ", command_buf.buf, 2)) + file_change_m(b); + else if (!strncmp("D ", command_buf.buf, 2)) + file_change_d(b); + else + die("Unsupported file_change: %s", command_buf.buf); + read_next_command(); + } + + /* build the tree and the commit */ + store_tree(&b->branch_tree); + hashcpy(b->branch_tree.versions[0].sha1, + b->branch_tree.versions[1].sha1); + size_dbuf(&new_data, 97 + msglen + + merge_count * 49 + + (author + ? strlen(author) + strlen(committer) + : 2 * strlen(committer))); + sp = new_data.buffer; + sp += sprintf(sp, "tree %s\n", + sha1_to_hex(b->branch_tree.versions[1].sha1)); + if (!is_null_sha1(b->sha1)) + sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1)); + while (merge_list) { + struct hash_list *next = merge_list->next; + sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1)); + free(merge_list); + merge_list = next; + } + if (author) + sp += sprintf(sp, "%s\n", author); + else + sp += sprintf(sp, "author %s\n", committer + 10); + sp += sprintf(sp, "%s\n\n", committer); + memcpy(sp, msg, msglen); + sp += msglen; + if (author) + free(author); + free(committer); + free(msg); + + store_object(OBJ_COMMIT, + new_data.buffer, sp - (char*)new_data.buffer, + NULL, b->sha1, next_mark); + b->last_commit = object_count_by_type[OBJ_COMMIT]; + + if (branch_log) { + int need_dq = quote_c_style(b->name, NULL, NULL, 0); + fprintf(branch_log, "commit "); + if (need_dq) { + fputc('"', branch_log); + quote_c_style(b->name, NULL, branch_log, 0); + fputc('"', branch_log); + } else + fprintf(branch_log, "%s", b->name); + fprintf(branch_log," :%lu %s\n",next_mark,sha1_to_hex(b->sha1)); + } +} + +static void cmd_new_tag() +{ + char *str_uq; + const char *endp; + char *sp; + const char *from; + char *tagger; + struct branch *s; + void *msg; + size_t msglen; + struct tag *t; + unsigned long from_mark = 0; + unsigned char sha1[20]; + + /* Obtain the new tag name from the rest of our command */ + sp = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(sp, &endp); + if (str_uq) { + if (*endp) + die("Garbage after tag name in: %s", command_buf.buf); + sp = str_uq; + } + t = pool_alloc(sizeof(struct tag)); + t->next_tag = NULL; + t->name = pool_strdup(sp); + if (last_tag) + last_tag->next_tag = t; + else + first_tag = t; + last_tag = t; + if (str_uq) + free(str_uq); + read_next_command(); + + /* from ... */ + if (strncmp("from ", command_buf.buf, 5)) + die("Expected from command, got %s", command_buf.buf); + + from = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(from, &endp); + if (str_uq) { + if (*endp) + die("Garbage after string in: %s", command_buf.buf); + from = str_uq; + } + + s = lookup_branch(from); + if (s) { + hashcpy(sha1, s->sha1); + } else if (*from == ':') { + from_mark = strtoul(from + 1, NULL, 10); + struct object_entry *oe = find_mark(from_mark); + if (oe->type != OBJ_COMMIT) + die("Mark :%lu not a commit", from_mark); + hashcpy(sha1, oe->sha1); + } else if (!get_sha1(from, sha1)) { + unsigned long size; + char *buf; + + buf = read_object_with_reference(sha1, + type_names[OBJ_COMMIT], &size, sha1); + if (!buf || size < 46) + die("Not a valid commit: %s", from); + free(buf); + } else + die("Invalid ref name or SHA1 expression: %s", from); + + if (str_uq) + free(str_uq); + read_next_command(); + + /* tagger ... */ + if (strncmp("tagger ", command_buf.buf, 7)) + die("Expected tagger command, got %s", command_buf.buf); + tagger = strdup(command_buf.buf); + + /* tag payload/message */ + read_next_command(); + msg = cmd_data(&msglen); + + /* build the tag object */ + size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen); + sp = new_data.buffer; + sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1)); + sp += sprintf(sp, "type %s\n", type_names[OBJ_COMMIT]); + sp += sprintf(sp, "tag %s\n", t->name); + sp += sprintf(sp, "%s\n\n", tagger); + memcpy(sp, msg, msglen); + sp += msglen; + free(tagger); + free(msg); + + store_object(OBJ_TAG, new_data.buffer, sp - (char*)new_data.buffer, + NULL, t->sha1, 0); + + if (branch_log) { + int need_dq = quote_c_style(t->name, NULL, NULL, 0); + fprintf(branch_log, "tag "); + if (need_dq) { + fputc('"', branch_log); + quote_c_style(t->name, NULL, branch_log, 0); + fputc('"', branch_log); + } else + fprintf(branch_log, "%s", t->name); + fprintf(branch_log," :%lu %s\n",from_mark,sha1_to_hex(t->sha1)); + } +} + +static void cmd_reset_branch() +{ + struct branch *b; + char *str_uq; + const char *endp; + char *sp; + + /* Obtain the branch name from the rest of our command */ + sp = strchr(command_buf.buf, ' ') + 1; + str_uq = unquote_c_style(sp, &endp); + if (str_uq) { + if (*endp) + die("Garbage after ref in: %s", command_buf.buf); + sp = str_uq; + } + b = lookup_branch(sp); + if (b) { + b->last_commit = 0; + if (b->branch_tree.tree) { + release_tree_content_recursive(b->branch_tree.tree); + b->branch_tree.tree = NULL; + } + } + else + b = new_branch(sp); + if (str_uq) + free(str_uq); + read_next_command(); + cmd_from(b); +} + +static const char fast_import_usage[] = +"git-fast-import [--objects=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file] [--branch-log=log] temp.pack"; + +int main(int argc, const char **argv) +{ + const char *base_name; + int i; + unsigned long est_obj_cnt = object_entry_alloc; + char *pack_name; + char *idx_name; + struct stat sb; + + setup_ident(); + git_config(git_default_config); + page_size = getpagesize(); + + for (i = 1; i < argc; i++) { + const char *a = argv[i]; + + if (*a != '-' || !strcmp(a, "--")) + break; + else if (!strncmp(a, "--objects=", 10)) + est_obj_cnt = strtoul(a + 10, NULL, 0); + else if (!strncmp(a, "--depth=", 8)) + max_depth = strtoul(a + 8, NULL, 0); + else if (!strncmp(a, "--active-branches=", 18)) + max_active_branches = strtoul(a + 18, NULL, 0); + else if (!strncmp(a, "--export-marks=", 15)) + mark_file = a + 15; + else if (!strncmp(a, "--branch-log=", 13)) { + branch_log = fopen(a + 13, "w"); + if (!branch_log) + die("Can't create %s: %s", a + 13, strerror(errno)); + } + else + die("unknown option %s", a); + } + if ((i+1) != argc) + usage(fast_import_usage); + base_name = argv[i]; + + pack_name = xmalloc(strlen(base_name) + 6); + sprintf(pack_name, "%s.pack", base_name); + idx_name = xmalloc(strlen(base_name) + 5); + sprintf(idx_name, "%s.idx", base_name); + + pack_fd = open(pack_name, O_RDWR|O_CREAT|O_EXCL, 0666); + if (pack_fd < 0) + die("Can't create %s: %s", pack_name, strerror(errno)); + + init_pack_header(); + alloc_objects(est_obj_cnt); + strbuf_init(&command_buf); + + atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); + branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); + avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); + marks = pool_calloc(1, sizeof(struct mark_set)); + + for (;;) { + read_next_command(); + if (command_buf.eof) + break; + else if (!strcmp("blob", command_buf.buf)) + cmd_new_blob(); + else if (!strncmp("commit ", command_buf.buf, 7)) + cmd_new_commit(); + else if (!strncmp("tag ", command_buf.buf, 4)) + cmd_new_tag(); + else if (!strncmp("reset ", command_buf.buf, 6)) + cmd_reset_branch(); + else + die("Unsupported command: %s", command_buf.buf); + } + + fixup_header_footer(); + close(pack_fd); + write_index(idx_name); + dump_branches(); + dump_tags(); + dump_marks(); + if (branch_log) + fclose(branch_log); + + fprintf(stderr, "%s statistics:\n", argv[0]); + fprintf(stderr, "---------------------------------------------------------------------\n"); + fprintf(stderr, "Alloc'd objects: %10lu (%10lu overflow )\n", alloc_count, alloc_count - est_obj_cnt); + fprintf(stderr, "Total objects: %10lu (%10lu duplicates )\n", object_count, duplicate_count); + fprintf(stderr, " blobs : %10lu (%10lu duplicates %10lu deltas)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB]); + fprintf(stderr, " trees : %10lu (%10lu duplicates %10lu deltas)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE]); + fprintf(stderr, " commits: %10lu (%10lu duplicates %10lu deltas)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT]); + fprintf(stderr, " tags : %10lu (%10lu duplicates %10lu deltas)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG]); + fprintf(stderr, "Total branches: %10lu (%10lu loads )\n", branch_count, branch_load_count); + fprintf(stderr, " marks: %10u (%10lu unique )\n", (1 << marks->shift) * 1024, marks_set_count); + fprintf(stderr, " atoms: %10u\n", atom_cnt); + fprintf(stderr, "Memory total: %10lu KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024); + fprintf(stderr, " pools: %10lu KiB\n", total_allocd/1024); + fprintf(stderr, " objects: %10lu KiB\n", (alloc_count*sizeof(struct object_entry))/1024); + fprintf(stderr, "Pack remaps: %10lu\n", remap_count); + stat(pack_name, &sb); + fprintf(stderr, "Pack size: %10lu KiB\n", (unsigned long)(sb.st_size/1024)); + stat(idx_name, &sb); + fprintf(stderr, "Index size: %10lu KiB\n", (unsigned long)(sb.st_size/1024)); + fprintf(stderr, "---------------------------------------------------------------------\n"); + + fprintf(stderr, "\n"); + + return 0; +}