Merge branch 'js/read-tree' into js/c-merge-recursive
authorJunio C Hamano <junkio@cox.net>
Mon, 31 Jul 2006 06:42:10 +0000 (23:42 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 31 Jul 2006 06:42:10 +0000 (23:42 -0700)
* js/read-tree: (107 commits)
read-tree: move merge functions to the library
read-trees: refactor the unpack_trees() part
tar-tree: illustrate an obscure feature better
git.c: allow alias expansion without a git directory
setup_git_directory_gently: do not barf when GIT_DIR is given.
Build on Debian GNU/kFreeBSD
Call setup_git_directory() much earlier
Call setup_git_directory() early
Display an error from update-ref if target ref name is invalid.
Fix http-fetch
t4103: fix binary patch application test.
git-apply -R: binary patches are irreversible for now.
Teach git-apply about '-R'
Makefile: ssh-pull.o depends on ssh-fetch.c
log and diff family: honor config even from subdirectories
git-reset: detect update-ref error and report it.
lost-found: use fsck-objects --full
Teach git-http-fetch the --stdin switch
Teach git-local-fetch the --stdin switch
Make pull() support fetching multiple targets at once
...

103 files changed:
.gitignore
Documentation/Makefile
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-daemon.txt
Documentation/git-format-patch.txt
Documentation/git-http-fetch.txt
Documentation/git-local-fetch.txt
Documentation/git-tar-tree.txt
Documentation/git.txt
Documentation/urls.txt
INSTALL
Makefile
blame.c
builtin-add.c
builtin-apply.c
builtin-cat-file.c
builtin-check-ref-format.c
builtin-commit-tree.c
builtin-count.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-stages.c
builtin-diff-tree.c
builtin-diff.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-help.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-mv.c [new file with mode: 0644]
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-rm.c
builtin-show-branch.c
builtin-stripspace.c
builtin-tar-tree.c
builtin-update-index.c
builtin-update-ref.c
builtin-upload-tar.c
builtin-write-tree.c
builtin.h
cache.h
combine-diff.c
config.c
config.mak.in [new file with mode: 0644]
configure.ac [new file with mode: 0644]
contrib/emacs/Makefile
contrib/emacs/git.el
daemon.c
diff.c
diff.h
environment.c
fetch.c
fetch.h
git-am.sh
git-clone.sh
git-cvsexportcommit.perl
git-cvsserver.perl
git-fetch.sh
git-instaweb.sh
git-lost-found.sh
git-merge.sh
git-mv.perl [deleted file]
git-pull.sh
git-quiltimport.sh
git-rebase.sh
git-reset.sh
git-resolve.sh
git-svn.perl
git.c
gitweb/gitweb.cgi
http-fetch.c
http-push.c
local-fetch.c
log-tree.c
pack-objects.c
read-cache.c
refs.c
revision.c
revision.h
setup.c
sha1_file.c
ssh-fetch.c
t/t1400-update-ref.sh
t/t4102-apply-rename.sh
t/t4103-apply-binary.sh
t/t4112-apply-renames.sh
t/t4114-apply-typechange.sh [new file with mode: 0755]
t/t6004-rev-list-path-optim.sh [new file with mode: 0755]
t/t7001-mv.sh
unpack-objects.c
unpack-trees.c [new file with mode: 0644]
unpack-trees.h [new file with mode: 0644]
upload-pack.c
index 52d61f31939d4fbbd55521cff85939fd1cf97e28..fb0fa3f16a7c8a42c5d0d1be62666cc4ea4c6575 100644 (file)
@@ -137,4 +137,10 @@ git-core.spec
 *.[ao]
 *.py[co]
 config.mak
+autom4te.cache
+config.log
+config.status
+config.mak.in
+config.mak.autogen
+configure
 git-blame
index 2b0efe7921b61fa3c3c84273054112373aac75ba..0d9ffb4ad9475de730ab2ee240d8b528b44017e8 100644 (file)
@@ -25,10 +25,10 @@ DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
 DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
 
 prefix?=$(HOME)
-bin=$(prefix)/bin
-mandir=$(prefix)/man
-man1=$(mandir)/man1
-man7=$(mandir)/man7
+bindir?=$(prefix)/bin
+mandir?=$(prefix)/man
+man1dir=$(mandir)/man1
+man7dir=$(mandir)/man7
 # DESTDIR=
 
 INSTALL?=install
@@ -46,15 +46,16 @@ all: html man
 
 html: $(DOC_HTML)
 
+$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN7): asciidoc.conf
 
 man: man1 man7
 man1: $(DOC_MAN1)
 man7: $(DOC_MAN7)
 
 install: man
-       $(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
-       $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
-       $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
+       $(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
+       $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir)
+       $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir)
 
 
 #
index 7ce71510de52805c2bfff59f4c13d8267b9e3f25..8196d787ab13475200f7c0e739b21f525ba9e4ae 100644 (file)
@@ -9,6 +9,8 @@
 
 [attributes]
 caret=^
+startsb=&#91;
+endsb=&#93;
 
 ifdef::backend-docbook[]
 [gitlink-inlinemacro]
index 0b434c1f1923fb10b2420a3295e59fd9d163f39b..465eb13e76dca5cf4daad9d5ea773f4743452c75 100644 (file)
@@ -97,6 +97,12 @@ core.compression::
        compression, and 1..9 are various speed/size tradeoffs, 9 being
        slowest.
 
+core.legacyheaders::
+       A boolean which enables the legacy object header format in case
+       you want to interoperate with old clients accessing the object
+       database directly (where the "http://" and "rsync://" protocols
+       count as direct access).
+
 alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -193,6 +199,10 @@ merge.summary::
        Whether to include summaries of merged commits in newly created
        merge commit messages. False by default.
 
+pack.window::
+       The size of the window used by gitlink:git-pack-objects[1] when no
+       window size is given on the command line. Defaults to 10.
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@ -208,6 +218,17 @@ showbranch.default::
        The default set of branches for gitlink:git-show-branch[1].
        See gitlink:git-show-branch[1].
 
+tar.umask::
+       By default, git-link:git-tar-tree[1] sets file and directories modes
+       to 0666 or 0777. While this is both useful and acceptable for projects
+       such as the Linux Kernel, it might be excessive for other projects.
+       With this variable, it becomes possible to tell
+       git-link:git-tar-tree[1] to apply a specific umask to the modes above.
+       The special value "user" indicates that the user's current umask will
+       be used. This should be enough for most projects, as it will lead to
+       the same permissions as git-link:git-checkout[1] would use. The default
+       value remains 0, which means world read-write.
+
 user.email::
        Your email address to be recorded in any newly created commits.
        Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL'
index 27ac72d98fcf8cf2cd0c0ab236e7646ecff7499d..092d0d6730294a5ce701522575bc1f8d91755a1d 100644 (file)
@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a commit to a CVS checkout
 
 SYNOPSIS
 --------
-'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-a] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
@@ -36,9 +36,13 @@ OPTIONS
        commit if any hunks fail to apply or there were other problems.
 
 -p::
-       Be pedantic (paranoid) when applying patches. Invokes patch with 
+       Be pedantic (paranoid) when applying patches. Invokes patch with
        --fuzz=0
 
+-a::
+       Add authorship information. Adds Author line, and Committer (if
+       different from Author) to the message.
+
 -f::
        Force the merge even if the files are not up to date.
 
index 4c357daf6ab23e3b3359388b101be8d363ae3be7..0f7d274eaba3722dabfdbf7ab14592a938933a56 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
-            [directory...]
+            [--reuseaddr] [--detach] [--pid-file=file] [directory...]
 
 DESCRIPTION
 -----------
@@ -82,6 +82,17 @@ OPTIONS
 --verbose::
        Log details about the incoming connections and requested files.
 
+--reuseaddr::
+       Use SO_REUSEADDR when binding the listening socket.
+       This allows the server to restart without waiting for
+       old connections to time out.
+
+--detach::
+       Detach from the shell. Implies --syslog.
+
+--pid-file=file::
+       Save the process id in 'file'.
+
 <directory>::
        A directory to add to the whitelist of allowed directories. Unless
        --strict-paths is specified this will also include subdirectories
index 4ca0014dac64729bee11b6a37bd730b538b2e5ee..67425dc0359ee9774200e1a2e12913d570117e7c 100644 (file)
@@ -9,8 +9,9 @@ git-format-patch - Prepare patches for e-mail submission
 SYNOPSIS
 --------
 [verse]
-'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach]
+'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach] [--thread]
                   [-s | --signoff] [--diff-options] [--start-number <n>]
+                  [--in-reply-to=Message-Id]
                   <since>[..<until>]
 
 DESCRIPTION
@@ -35,6 +36,10 @@ they are created in the current working directory.
 If -n is specified, instead of "[PATCH] Subject", the first line
 is formatted as "[PATCH n/m] Subject".
 
+If given --thread, git-format-patch will generate In-Reply-To and
+References headers to make the second and subsequent patch mails appear
+as replies to the first mail; this also generates a Message-Id header to
+reference.
 
 OPTIONS
 -------
@@ -63,6 +68,15 @@ OPTIONS
 --attach::
        Create attachments instead of inlining patches.
 
+--thread::
+       Add In-Reply-To and References headers to make the second and
+       subsequent mails appear as replies to the first.  Also generates
+       the Message-Id header to reference.
+
+--in-reply-to=Message-Id::
+       Make the first mail (or all the mails with --no-thread) appear as a
+       reply to the given Message-Id, which avoids breaking threads to
+       provide a new patch series.
 
 CONFIGURATION
 -------------
index bc1a13289153976c31718d3694de2a188d5b8478..3d508094afc6a79d40b84ce0a3380f487a40d5e8 100644 (file)
@@ -8,7 +8,7 @@ git-http-fetch - downloads a remote git repository via HTTP
 
 SYNOPSIS
 --------
-'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] <commit> <url>
+'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] <commit> <url>
 
 DESCRIPTION
 -----------
@@ -33,6 +33,12 @@ commit-id::
         Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
+--stdin::
+       Instead of a commit id on the commandline (which is not expected in this
+       case), 'git-http-fetch' expects lines on stdin in the format
+
+               <commit-id>['\t'<filename-as-in--w>]
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 87abec1c4e46ad53e018264046091af4455da307..2fbdfe086a4c974d19c3d8f4c6a1722728b0114a 100644 (file)
@@ -29,6 +29,12 @@ OPTIONS
         Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
+--stdin::
+       Instead of a commit id on the commandline (which is not expected in this
+       case), 'git-local-fetch' expects lines on stdin in the format
+
+               <commit-id>['\t'<filename-as-in--w>]
+
 Author
 ------
 Written by Junio C Hamano <junkio@cox.net>
index f2675c41933fc4f824f871cc9185a6a1bf010d2b..1e1c7fa856a2b80bb839da805aa57d43f65f968e 100644 (file)
@@ -37,7 +37,20 @@ OPTIONS
        Instead of making a tar archive from local repository,
        retrieve a tar archive from a remote repository.
 
-Examples
+CONFIGURATION
+-------------
+By default, file and directories modes are set to 0666 or 0777. It is
+possible to change this by setting the "umask" variable in the
+repository configuration as follows :
+
+[tar]
+        umask = 002    ;# group friendly
+
+The special umask value "user" indicates that the user's current umask
+will be used instead. The default value remains 0, which means world
+readable/writable files and directories.
+
+EXAMPLES
 --------
 git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)::
 
@@ -58,6 +71,11 @@ git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar::
 
        Get a tarball v1.4.0 from example.com.
 
+git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar::
+
+       Put everything in the current head's Documentation/ directory
+       into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'.
+
 Author
 ------
 Written by Rene Scharfe.
index ce3058182fead833c45daf02a2806f151b15f61f..7310a2b8b8bc474f53d498d254cab772a8ef45ae 100644 (file)
@@ -8,7 +8,8 @@ git - the stupid content tracker
 
 SYNOPSIS
 --------
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS]
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
+       [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -41,6 +42,15 @@ OPTIONS
        environment variable. If no path is given 'git' will print
        the current setting and then exit.
 
+-p|--paginate::
+       Pipe all output into 'less' (or if set, $PAGER).
+
+--git-dir=<path>::
+       Set the path to the repository. This can also be controlled by
+       setting the GIT_DIR environment variable.
+
+--bare::
+       Same as --git-dir=`pwd`.
 
 FURTHER DOCUMENTATION
 ---------------------
index 93378d23789151eedc26834df257d117fe66f5ce..26ecba53fbfad4af4da46680330f998bf92f1b3f 100644 (file)
@@ -10,20 +10,21 @@ to name the remote repository:
 - https://host.xz/path/to/repo.git/
 - git://host.xz/path/to/repo.git/
 - git://host.xz/~user/path/to/repo.git/
-- ssh://+++[user@+++]host.xz/path/to/repo.git/
-- ssh://+++[user@+++]host.xz/~user/path/to/repo.git/
-- ssh://+++[user@+++]host.xz/~/path/to/repo.git
+- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
 ===============================================================
 
-SSH Is the default transport protocol and also supports an
-scp-like syntax.  Both syntaxes support username expansion,
+SSH is the default transport protocol.  You can optionally specify
+which user to log-in as, and an alternate, scp-like syntax is also
+supported.  Both syntaxes support username expansion,
 as does the native git protocol. The following three are
 identical to the last three above, respectively:
 
 ===============================================================
-- host.xz:/path/to/repo.git/
-- host.xz:~user/path/to/repo.git/
-- host.xz:path/to/repo.git
+- {startsb}user@{endsb}host.xz:/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:~user/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:path/to/repo.git
 ===============================================================
 
 To sync with a local directory, use:
diff --git a/INSTALL b/INSTALL
index 7da2c8982978e9ff069eb1cc2338330a45669100..ba9778cd4de187878b6445be5fbf490d76879f30 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -13,6 +13,15 @@ that uses $prefix, the built results have some paths encoded,
 which are derived from $prefix, so "make all; make prefix=/usr
 install" would not work.
 
+Alternatively you can use autoconf generated ./configure script to
+set up install paths (via config.mak.autogen), so you can write instead
+
+       $ autoconf ;# as yourself if ./configure doesn't exist yet
+       $ ./configure --prefix=/usr ;# as yourself
+       $ make all doc ;# as yourself
+       # make install install-doc ;# as root
+
+
 Issues of note:
 
  - git normally installs a helper script wrapper called "git", which
index 12ffd45ca9cd9eb70092b4c4637bdd7b616e5372..b7a095f0dd06791bf95194fbc27922abf1a91a1d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,18 @@ all:
 # tests.  These tests take up a significant amount of the total test time
 # but are not needed unless you plan to talk to SVN repos.
 #
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want GIT to link against any libraries
+# installed there.  If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want GIT to
+# link against any libraries installed there.  If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -104,6 +116,8 @@ template_dir = $(prefix)/share/git-core/templates/
 GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
 
+export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
+
 CC = gcc
 AR = ar
 TAR = tar
@@ -137,7 +151,7 @@ SCRIPT_PERL = \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
        git-shortlog.perl git-rerere.perl \
        git-annotate.perl git-cvsserver.perl \
-       git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
+       git-svnimport.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
 
 SCRIPT_PYTHON = \
@@ -179,7 +193,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
        git-read-tree$X git-commit-tree$X git-write-tree$X \
        git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
        git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
-       git-fmt-merge-msg$X git-prune$X
+       git-fmt-merge-msg$X git-prune$X git-mv$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -208,7 +222,7 @@ 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 \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-       tree-walk.h log-tree.h dir.h
+       tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h
 
 DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -223,7 +237,7 @@ LIB_OBJS = \
        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 \
-       alloc.o merge-file.o $(DIFF_OBJS)
+       alloc.o merge-file.o path-list.o unpack-trees.o $(DIFF_OBJS)
 
 BUILTIN_OBJS = \
        builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
@@ -235,7 +249,8 @@ BUILTIN_OBJS = \
        builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
        builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
        builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
-       builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o
+       builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \
+       builtin-mv.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -251,19 +266,24 @@ LIBS = $(GITLIBS) -lz
 ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
 endif
+ifeq ($(uname_S),GNU/kFreeBSD)
+       NO_STRLCPY = YesPlease
+endif
 ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_STRLCPY = YesPlease
-       ## fink
-       ifeq ($(shell test -d /sw/lib && echo y),y)
-               ALL_CFLAGS += -I/sw/include
-               ALL_LDFLAGS += -L/sw/lib
+       ifndef NO_FINK
+               ifeq ($(shell test -d /sw/lib && echo y),y)
+                       ALL_CFLAGS += -I/sw/include
+                       ALL_LDFLAGS += -L/sw/lib
+               endif
        endif
-       ## darwinports
-       ifeq ($(shell test -d /opt/local/lib && echo y),y)
-               ALL_CFLAGS += -I/opt/local/include
-               ALL_LDFLAGS += -L/opt/local/lib
+       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)
@@ -337,6 +357,7 @@ ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
 endif
 
+-include config.mak.autogen
 -include config.mak
 
 ifdef WITH_OWN_SUBPROCESS_PY
@@ -562,7 +583,7 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
            -e '/@@GITWEB_CGI@@/d' \
            -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
            -e '/@@GITWEB_CSS@@/d' \
-           $@.sh > $@+
+           $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+
        chmod +x $@+
        mv $@+ $@
 
@@ -599,6 +620,8 @@ $(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
 git-ssh-fetch$X: rsh.o fetch.o
 git-ssh-upload$X: rsh.o
@@ -745,8 +768,8 @@ dist-doc:
        rm -fr .doc-tmp-dir
        mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
        $(MAKE) -C Documentation DESTDIR=./ \
-               man1=../.doc-tmp-dir/man1 \
-               man7=../.doc-tmp-dir/man7 \
+               man1dir=../.doc-tmp-dir/man1 \
+               man7dir=../.doc-tmp-dir/man7 \
                install
        cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
        gzip -n -9 -f $(manpages).tar
@@ -759,6 +782,8 @@ clean:
                $(LIB_FILE) $(XDIFF_LIB)
        rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
+       rm -rf autom4te.cache
+       rm -f config.log config.mak.autogen configure config.status config.cache
        rm -rf $(GIT_TARNAME) .doc-tmp-dir
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        rm -f $(htmldocs).tar.gz $(manpages).tar.gz
diff --git a/blame.c b/blame.c
index b04b8f58aaf718208ab13e5cbfd8cd2a4444cbd7..76712b5962bcb0f511cc4a18766d1a45dc901675 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -834,7 +834,7 @@ int main(int argc, const char **argv)
        }
 
 
-       init_revisions(&rev);
+       init_revisions(&rev, setup_git_directory());
        rev.remove_empty_trees = 1;
        rev.topo_order = 1;
        rev.prune_fn = simplify_commit;
index 2d256981733345f4a430e11812b6851f0351aabd..f548b8007de79d66f7ed3fad0d96bce75c6bbf57 100644 (file)
@@ -21,8 +21,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
 
        for (specs = 0; pathspec[specs];  specs++)
                /* nothing */;
-       seen = xmalloc(specs);
-       memset(seen, 0, specs);
+       seen = xcalloc(specs, 1);
 
        src = dst = dir->entries;
        i = dir->nr;
@@ -83,52 +82,12 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
                prune_directory(dir, pathspec, baselen);
 }
 
-static int add_file_to_index(const char *path, int verbose)
-{
-       int size, namelen;
-       struct stat st;
-       struct cache_entry *ce;
-
-       if (lstat(path, &st))
-               die("%s: unable to stat (%s)", path, strerror(errno));
-
-       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
-               die("%s: can only add regular files or symbolic links", path);
-
-       namelen = strlen(path);
-       size = cache_entry_size(namelen);
-       ce = xcalloc(1, size);
-       memcpy(ce->name, path, namelen);
-       ce->ce_flags = htons(namelen);
-       fill_stat_cache_info(ce, &st);
-
-       ce->ce_mode = create_ce_mode(st.st_mode);
-       if (!trust_executable_bit) {
-               /* If there is an existing entry, pick the mode bits
-                * from it.
-                */
-               int pos = cache_name_pos(path, namelen);
-               if (pos >= 0)
-                       ce->ce_mode = active_cache[pos]->ce_mode;
-       }
-
-       if (index_path(ce->sha1, path, &st, 1))
-               die("unable to index file %s", path);
-       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
-               die("unable to add %s to index",path);
-       if (verbose)
-               printf("add '%s'\n", path);
-       cache_tree_invalidate_path(active_cache_tree, path);
-       return 0;
-}
-
 static struct lock_file lock_file;
 
-int cmd_add(int argc, const char **argv, char **envp)
+int cmd_add(int argc, const char **argv, const char *prefix)
 {
        int i, newfd;
        int verbose = 0, show_only = 0;
-       const char *prefix = setup_git_directory();
        const char **pathspec;
        struct dir_struct dir;
 
@@ -160,7 +119,6 @@ int cmd_add(int argc, const char **argv, char **envp)
                }
                die(builtin_add_usage);
        }
-       git_config(git_default_config);
        pathspec = get_pathspec(prefix, argv + i);
 
        fill_directory(&dir, pathspec);
index c903146bb65e6db4dcb527badf864d98db6aabb3..f8c6763c7464a01ea81fae0665a7aef50809d031 100644 (file)
@@ -120,7 +120,7 @@ struct fragment {
 struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
-       int is_rename, is_copy, is_new, is_delete, is_binary;
+       int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
 #define BINARY_DELTA_DEFLATED 1
 #define BINARY_LITERAL_DEFLATED 2
        unsigned long deflate_origlen;
@@ -1119,6 +1119,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
        return offset + hdrsize + patchsize;
 }
 
+#define swap(a,b) myswap((a),(b),sizeof(a))
+
+#define myswap(a, b, size) do {                \
+       unsigned char mytmp[size];      \
+       memcpy(mytmp, &a, size);                \
+       memcpy(&a, &b, size);           \
+       memcpy(&b, mytmp, size);                \
+} while (0)
+
+static void reverse_patches(struct patch *p)
+{
+       for (; p; p = p->next) {
+               struct fragment *frag = p->fragments;
+
+               swap(p->new_name, p->old_name);
+               swap(p->new_mode, p->old_mode);
+               swap(p->is_new, p->is_delete);
+               swap(p->lines_added, p->lines_deleted);
+               swap(p->old_sha1_prefix, p->new_sha1_prefix);
+
+               for (; frag; frag = frag->next) {
+                       swap(frag->newpos, frag->oldpos);
+                       swap(frag->newlines, frag->oldlines);
+               }
+               p->is_reverse = !p->is_reverse;
+       }
+}
+
 static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
 static const char minuses[]= "----------------------------------------------------------------------";
 
@@ -1336,7 +1364,7 @@ static int apply_line(char *output, const char *patch, int plen)
 }
 
 static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
-       int inaccurate_eof)
+       int reverse, int inaccurate_eof)
 {
        int match_beginning, match_end;
        char *buf = desc->buffer;
@@ -1350,6 +1378,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
        int pos, lines;
 
        while (size > 0) {
+               char first;
                int len = linelen(patch, size);
                int plen;
 
@@ -1366,16 +1395,23 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                plen = len-1;
                if (len < size && patch[len] == '\\')
                        plen--;
-               switch (*patch) {
+               first = *patch;
+               if (reverse) {
+                       if (first == '-')
+                               first = '+';
+                       else if (first == '+')
+                               first = '-';
+               }
+               switch (first) {
                case ' ':
                case '-':
                        memcpy(old + oldsize, patch + 1, plen);
                        oldsize += plen;
-                       if (*patch == '-')
+                       if (first == '-')
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (*patch != '+' || !no_add)
+                       if (first != '+' || !no_add)
                                newsize += apply_line(new + newsize, patch,
                                                      plen);
                        break;
@@ -1499,6 +1535,12 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
        void *data;
        void *result;
 
+       /* Binary patch is irreversible */
+       if (patch->is_reverse)
+               return error("cannot reverse-apply a binary patch to '%s'",
+                            patch->new_name
+                            ? patch->new_name : patch->old_name);
+
        data = inflate_it(fragment->patch, fragment->size,
                          patch->deflate_origlen);
        if (!data)
@@ -1615,7 +1657,8 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
                return apply_binary(desc, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
+               if (apply_one_fragment(desc, frag, patch->is_reverse,
+                                       patch->inaccurate_eof) < 0)
                        return error("patch failed: %s:%ld",
                                     name, frag->oldpos);
                frag = frag->next;
@@ -1664,13 +1707,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        return 0;
 }
 
-static int check_patch(struct patch *patch)
+static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
        struct stat st;
        const char *old_name = patch->old_name;
        const char *new_name = patch->new_name;
        const char *name = old_name ? old_name : new_name;
        struct cache_entry *ce = NULL;
+       int ok_if_exists;
 
        if (old_name) {
                int changed = 0;
@@ -1728,13 +1772,33 @@ static int check_patch(struct patch *patch)
                                old_name, st_mode, patch->old_mode);
        }
 
+       if (new_name && prev_patch && prev_patch->is_delete &&
+           !strcmp(prev_patch->old_name, new_name))
+               /* A type-change diff is always split into a patch to
+                * delete old, immediately followed by a patch to
+                * create new (see diff.c::run_diff()); in such a case
+                * it is Ok that the entry to be deleted by the
+                * previous patch is still in the working tree and in
+                * the index.
+                */
+               ok_if_exists = 1;
+       else
+               ok_if_exists = 0;
+
        if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
-               if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+               if (check_index &&
+                   cache_name_pos(new_name, strlen(new_name)) >= 0 &&
+                   !ok_if_exists)
                        return error("%s: already exists in index", new_name);
                if (!cached) {
-                       if (!lstat(new_name, &st))
-                               return error("%s: already exists in working directory", new_name);
-                       if (errno != ENOENT)
+                       struct stat nst;
+                       if (!lstat(new_name, &nst)) {
+                               if (S_ISDIR(nst.st_mode) || ok_if_exists)
+                                       ; /* ok */
+                               else
+                                       return error("%s: already exists in working directory", new_name);
+                       }
+                       else if ((errno != ENOENT) && (errno != ENOTDIR))
                                return error("%s: %s", new_name, strerror(errno));
                }
                if (!patch->new_mode) {
@@ -1762,10 +1826,13 @@ static int check_patch(struct patch *patch)
 
 static int check_patch_list(struct patch *patch)
 {
+       struct patch *prev_patch = NULL;
        int error = 0;
 
-       for (;patch ; patch = patch->next)
-               error |= check_patch(patch);
+       for (prev_patch = NULL; patch ; patch = patch->next) {
+               error |= check_patch(patch, prev_patch);
+               prev_patch = patch;
+       }
        return error;
 }
 
@@ -2010,6 +2077,16 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        return;
        }
 
+       if (errno == EEXIST || errno == EACCES) {
+               /* We may be trying to create a file where a directory
+                * used to be.
+                */
+               struct stat st;
+               errno = 0;
+               if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+                       errno = EEXIST;
+       }
+
        if (errno == EEXIST) {
                unsigned int nr = getpid();
 
@@ -2044,32 +2121,42 @@ static void create_file(struct patch *patch)
        cache_tree_invalidate_path(active_cache_tree, path);
 }
 
-static void write_out_one_result(struct patch *patch)
+/* phase zero is to remove, phase one is to create */
+static void write_out_one_result(struct patch *patch, int phase)
 {
        if (patch->is_delete > 0) {
-               remove_file(patch);
+               if (phase == 0)
+                       remove_file(patch);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
-               create_file(patch);
+               if (phase == 1)
+                       create_file(patch);
                return;
        }
        /*
         * Rename or modification boils down to the same
         * thing: remove the old, write the new
         */
-       remove_file(patch);
+       if (phase == 0)
+               remove_file(patch);
+       if (phase == 1)
        create_file(patch);
 }
 
 static void write_out_results(struct patch *list, int skipped_patch)
 {
+       int phase;
+
        if (!list && !skipped_patch)
                die("No changes");
 
-       while (list) {
-               write_out_one_result(list);
-               list = list->next;
+       for (phase = 0; phase < 2; phase++) {
+               struct patch *l = list;
+               while (l) {
+                       write_out_one_result(l, phase);
+                       l = l->next;
+               }
        }
 }
 
@@ -2098,7 +2185,8 @@ static int use_patch(struct patch *p)
        return 1;
 }
 
-static int apply_patch(int fd, const char *filename, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename,
+               int reverse, int inaccurate_eof)
 {
        unsigned long offset, size;
        char *buffer = read_patch_file(fd, &size);
@@ -2118,6 +2206,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
                nr = parse_chunk(buffer + offset, size, patch);
                if (nr < 0)
                        break;
+               if (reverse)
+                       reverse_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
                        *listp = patch;
@@ -2178,10 +2268,11 @@ static int git_apply_config(const char *var, const char *value)
 }
 
 
-int cmd_apply(int argc, const char **argv, char **envp)
+int cmd_apply(int argc, const char **argv, const char *prefix)
 {
        int i;
        int read_stdin = 1;
+       int reverse = 0;
        int inaccurate_eof = 0;
 
        const char *whitespace_option = NULL;
@@ -2192,7 +2283,7 @@ int cmd_apply(int argc, const char **argv, char **envp)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0, "<stdin>", inaccurate_eof);
+                       apply_patch(0, "<stdin>", reverse, inaccurate_eof);
                        read_stdin = 0;
                        continue;
                }
@@ -2269,6 +2360,10 @@ int cmd_apply(int argc, const char **argv, char **envp)
                        parse_whitespace_option(arg + 13);
                        continue;
                }
+               if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
+                       reverse = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--inaccurate-eof")) {
                        inaccurate_eof = 1;
                        continue;
@@ -2289,12 +2384,12 @@ int cmd_apply(int argc, const char **argv, char **envp)
                        usage(apply_usage);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
-               apply_patch(fd, arg, inaccurate_eof);
+               apply_patch(fd, arg, reverse, inaccurate_eof);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0, "<stdin>", inaccurate_eof);
+               apply_patch(0, "<stdin>", reverse, inaccurate_eof);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
index 4d36817e5fed0e50f84eb48ac183b0c377596a86..814fb0743f7a453114991af6c28bb69ef44d4883 100644 (file)
@@ -94,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
        return 0;
 }
 
-int cmd_cat_file(int argc, const char **argv, char **envp)
+int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
        unsigned char sha1[20];
        char type[20];
@@ -102,7 +102,6 @@ int cmd_cat_file(int argc, const char **argv, char **envp)
        unsigned long size;
        int opt;
 
-       setup_git_directory();
        git_config(git_default_config);
        if (argc != 3)
                usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
index 4a23936aff8c21ca42a1b1e460543b1c775ccb45..701de439ae0f508f3a8ab41559230357be60637e 100644 (file)
@@ -6,7 +6,7 @@
 #include "refs.h"
 #include "builtin.h"
 
-int cmd_check_ref_format(int argc, const char **argv, char **envp)
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
        if (argc != 2)
                usage("git check-ref-format refname");
index ec082bf754a0ce5ae1615c41ba32c4515a927361..9c987966711d4bff1f709df0c14071170950e21d 100644 (file)
@@ -77,7 +77,7 @@ static int new_parent(int idx)
        return 1;
 }
 
-int cmd_commit_tree(int argc, const char **argv, char **envp)
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 {
        int i;
        int parents = 0;
@@ -88,8 +88,6 @@ int cmd_commit_tree(int argc, const char **argv, char **envp)
        unsigned int size;
 
        setup_ident();
-       setup_git_directory();
-
        git_config(git_default_config);
 
        if (argc < 2)
index 5ee72df247245cbf5fc8d5b70e8b1fddd5ab7edd..1d3729aa9914d6c68dbb6ad11cb46504b4a688e0 100644 (file)
@@ -67,7 +67,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
        }
 }
 
-int cmd_count_objects(int ac, const char **av, char **ep)
+int cmd_count_objects(int ac, const char **av, const char *prefix)
 {
        int i;
        int verbose = 0;
index 81ac2fe64aea23f9db605da181da098dcfca757a..ac13db70ff741240f1f27f98dfe09614da4ea87c 100644 (file)
@@ -13,13 +13,13 @@ static const char diff_files_usage[] =
 "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_files(int argc, const char **argv, char **envp)
+int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        int silent = 0;
 
+       init_revisions(&rev, prefix);
        git_config(git_default_config); /* no "diff" UI options */
-       init_revisions(&rev);
        rev.abbrev = 0;
 
        argc = setup_revisions(argc, argv, &rev, NULL);
index a1fa1b85cf741cd6b5f06afd02abbe618e86f5d1..95a3db156bda2d1e8d2ad7bdf72a9f9a821c6f51 100644 (file)
@@ -9,14 +9,14 @@ static const char diff_cache_usage[] =
 "[<common diff options>] <tree-ish> [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_index(int argc, const char **argv, char **envp)
+int cmd_diff_index(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        int cached = 0;
        int i;
 
+       init_revisions(&rev, prefix);
        git_config(git_default_config); /* no "diff" UI options */
-       init_revisions(&rev);
        rev.abbrev = 0;
 
        argc = setup_revisions(argc, argv, &rev, NULL);
index 9c62702941569511a149eba89a8be2b478f04b70..5960e089975f1f27c55c5bd8ea1c968ffe695d1f 100644 (file)
@@ -55,10 +55,9 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
        }
 }
 
-int cmd_diff_stages(int ac, const char **av, char **envp)
+int cmd_diff_stages(int ac, const char **av, const char *prefix)
 {
        int stage1, stage2;
-       const char *prefix = setup_git_directory();
        const char **pathspec = NULL;
 
        git_config(git_default_config); /* no "diff" UI options */
index b6106685942e3cb3a5db2b3a67123b5bd554d856..24cb2d7f84064d7833fa04f33aeca6eafc8b931d 100644 (file)
@@ -59,7 +59,7 @@ static const char diff_tree_usage[] =
 "  --root        include the initial commit as diff against /dev/null\n"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_tree(int argc, const char **argv, char **envp)
+int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 {
        int nr_sha1;
        char line[1000];
@@ -67,9 +67,9 @@ int cmd_diff_tree(int argc, const char **argv, char **envp)
        static struct rev_info *opt = &log_tree_opt;
        int read_stdin = 0;
 
+       init_revisions(opt, prefix);
        git_config(git_default_config); /* no "diff" UI options */
        nr_sha1 = 0;
-       init_revisions(opt);
        opt->abbrev = 0;
        opt->diff = 1;
        argc = setup_revisions(argc, argv, opt, NULL);
index cb38f445611735cff5bc9fbafb3189e6ccc6c2fd..48d2fd03b79c9c03e1e32b7743f936872a3684f1 100644 (file)
@@ -221,7 +221,7 @@ void add_head(struct rev_info *revs)
        add_pending_object(revs, obj, "HEAD");
 }
 
-int cmd_diff(int argc, const char **argv, char **envp)
+int cmd_diff(int argc, const char **argv, const char *prefix)
 {
        int i;
        struct rev_info rev;
@@ -251,7 +251,7 @@ int cmd_diff(int argc, const char **argv, char **envp)
         */
 
        git_config(git_diff_ui_config);
-       init_revisions(&rev);
+       init_revisions(&rev, prefix);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
@@ -346,7 +346,15 @@ int cmd_diff(int argc, const char **argv, char **envp)
                return builtin_diff_index(&rev, argc, argv);
        else if (ents == 2)
                return builtin_diff_tree(&rev, argc, argv, ent);
+       else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
+               /* diff A...B where there is one sane merge base between
+                * A and B.  We have ent[0] == merge-base, ent[1] == A,
+                * and ent[2] == B.  Show diff between the base and B.
+                */
+               return builtin_diff_tree(&rev, argc, argv, ent);
+       }
        else
-               return builtin_diff_combined(&rev, argc, argv, ent, ents);
+               return builtin_diff_combined(&rev, argc, argv,
+                                            ent, ents);
        usage(builtin_diff_usage);
 }
index f20b27b8a9cfd78ad5ae8ba67f08ec25849394e2..c84224ee84803cbe4766b9e61da16da718a6eb15 100644 (file)
@@ -242,7 +242,7 @@ static void shortlog(const char *name, unsigned char *sha1,
        free_list(&subjects);
 }
 
-int cmd_fmt_merge_msg(int argc, char **argv, char **envp)
+int cmd_fmt_merge_msg(int argc, char **argv, const char *prefix)
 {
        int limit = 20, i = 0;
        char line[1024];
@@ -342,7 +342,7 @@ int cmd_fmt_merge_msg(int argc, char **argv, char **envp)
                struct rev_info rev;
 
                head = lookup_commit(head_sha1);
-               init_revisions(&rev);
+               init_revisions(&rev, prefix);
                rev.commit_format = CMIT_FMT_ONELINE;
                rev.ignore_merges = 1;
                rev.limited = 1;
index a79bac305aea880d6decb787836fe86295ead32e..69b7c4862af3bfcbc9aea2cb52abaa65b3e8490b 100644 (file)
@@ -919,14 +919,13 @@ static const char emsg_missing_context_len[] =
 static const char emsg_missing_argument[] =
 "option requires an argument -%s";
 
-int cmd_grep(int argc, const char **argv, char **envp)
+int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
        int cached = 0;
        int seen_dashdash = 0;
        struct grep_opt opt;
        struct object_array list = { 0, 0, NULL };
-       const char *prefix = setup_git_directory();
        const char **paths = NULL;
        int i;
 
index 335fe5fedcc6523a3b49a3e00686fe381098bcda..bb0b03f1ae64cc5f4fa1376169c27b8a7cb71734 100644 (file)
@@ -221,15 +221,15 @@ static void show_man_page(const char *git_cmd)
        execlp("man", "man", page, NULL);
 }
 
-int cmd_version(int argc, const char **argv, char **envp)
+int cmd_version(int argc, const char **argv, const char *prefix)
 {
        printf("git version %s\n", git_version_string);
        return 0;
 }
 
-int cmd_help(int argc, const char **argv, char **envp)
+int cmd_help(int argc, const char **argv, const char *prefix)
 {
-       const char *help_cmd = argv[1];
+       const char *help_cmd = argc > 1 ? argv[1] : NULL;
        if (!help_cmd)
                cmd_usage(0, git_exec_path(), NULL);
        else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a"))
index 7fdd2fa9f9f7fb2801c6e1fc398f78b414a269e1..52473edf569e4d318a74548acefaf4023f37c87c 100644 (file)
@@ -250,7 +250,7 @@ static const char init_db_usage[] =
  * On the other hand, it might just make lookup slower and messier. You
  * be the judge.  The default case is to have one DB per managed directory.
  */
-int cmd_init_db(int argc, const char **argv, char **envp)
+int cmd_init_db(int argc, const char **argv, const char *prefix)
 {
        const char *git_dir;
        const char *sha1_dir;
index 7e5cab15c106f59417804a25d094c94e78d64063..82c69d1d0593156c5f7034746fe68f5a1a9f7276 100644 (file)
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include <time.h>
+#include <sys/time.h>
 
 /* this is in builtin-diff.c */
 void add_head(struct rev_info *revs);
 
-static void cmd_log_init(int argc, const char **argv, char **envp,
+static void cmd_log_init(int argc, const char **argv, const char *prefix,
                      struct rev_info *rev)
 {
        rev->abbrev = DEFAULT_ABBREV;
@@ -43,27 +45,27 @@ static int cmd_log_walk(struct rev_info *rev)
        return 0;
 }
 
-int cmd_whatchanged(int argc, const char **argv, char **envp)
+int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
 
        git_config(git_diff_ui_config);
-       init_revisions(&rev);
+       init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.diffopt.recursive = 1;
        rev.simplify_history = 0;
-       cmd_log_init(argc, argv, envp, &rev);
+       cmd_log_init(argc, argv, prefix, &rev);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
        return cmd_log_walk(&rev);
 }
 
-int cmd_show(int argc, const char **argv, char **envp)
+int cmd_show(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
 
        git_config(git_diff_ui_config);
-       init_revisions(&rev);
+       init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.diffopt.recursive = 1;
        rev.combine_merges = 1;
@@ -71,18 +73,18 @@ int cmd_show(int argc, const char **argv, char **envp)
        rev.always_show_header = 1;
        rev.ignore_merges = 0;
        rev.no_walk = 1;
-       cmd_log_init(argc, argv, envp, &rev);
+       cmd_log_init(argc, argv, prefix, &rev);
        return cmd_log_walk(&rev);
 }
 
-int cmd_log(int argc, const char **argv, char **envp)
+int cmd_log(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
 
        git_config(git_diff_ui_config);
-       init_revisions(&rev);
+       init_revisions(&rev, prefix);
        rev.always_show_header = 1;
-       cmd_log_init(argc, argv, envp, &rev);
+       cmd_log_init(argc, argv, prefix, &rev);
        return cmd_log_walk(&rev);
 }
 
@@ -176,7 +178,7 @@ static int get_patch_id(struct commit *commit, struct diff_options *options,
        return diff_flush_patch_id(options, sha1);
 }
 
-static void get_patch_ids(struct rev_info *rev, struct diff_options *options)
+static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)
 {
        struct rev_info check_rev;
        struct commit *commit;
@@ -201,7 +203,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options)
                die("diff_setup_done failed");
 
        /* given a range a..b get all patch ids for b..a */
-       init_revisions(&check_rev);
+       init_revisions(&check_rev, prefix);
        o1->flags ^= UNINTERESTING;
        o2->flags ^= UNINTERESTING;
        add_pending_object(&check_rev, o1, "o1");
@@ -226,7 +228,19 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options)
        o2->flags = flags2;
 }
 
-int cmd_format_patch(int argc, const char **argv, char **envp)
+static void gen_message_id(char *dest, unsigned int length, char *base)
+{
+       const char *committer = git_committer_info(1);
+       const char *email_start = strrchr(committer, '<');
+       const char *email_end = strrchr(committer, '>');
+       if(!email_start || !email_end || email_start > email_end - 1)
+               die("Could not extract email from committer identity.");
+       snprintf(dest, length, "%s.%lu.git.%.*s", base,
+                (unsigned long) time(NULL),
+                (int)(email_end - email_start - 1), email_start + 1);
+}
+
+int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
        struct commit **list = NULL;
@@ -237,11 +251,15 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        int start_number = -1;
        int keep_subject = 0;
        int ignore_if_in_upstream = 0;
+       int thread = 0;
+       const char *in_reply_to = NULL;
        struct diff_options patch_id_opts;
        char *add_signoff = NULL;
+       char message_id[1024];
+       char ref_message_id[1024];
 
        git_config(git_format_config);
-       init_revisions(&rev);
+       init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
@@ -304,6 +322,16 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
                        rev.mime_boundary = argv[i] + 9;
                else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
                        ignore_if_in_upstream = 1;
+               else if (!strcmp(argv[i], "--thread"))
+                       thread = 1;
+               else if (!strncmp(argv[i], "--in-reply-to=", 14))
+                       in_reply_to = argv[i] + 14;
+               else if (!strcmp(argv[i], "--in-reply-to")) {
+                       i++;
+                       if (i == argc)
+                               die("Need a Message-Id for --in-reply-to");
+                       in_reply_to = argv[i];
+               }
                else
                        argv[j++] = argv[i];
        }
@@ -335,7 +363,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        }
 
        if (ignore_if_in_upstream)
-               get_patch_ids(&rev, &patch_id_opts);
+               get_patch_ids(&rev, &patch_id_opts, prefix);
 
        if (!use_stdout)
                realstdout = fdopen(dup(1), "w");
@@ -361,10 +389,23 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        if (numbered)
                rev.total = total + start_number - 1;
        rev.add_signoff = add_signoff;
+       rev.ref_message_id = in_reply_to;
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
                rev.nr = total - nr + (start_number - 1);
+               /* Make the second and subsequent mails replies to the first */
+               if (thread) {
+                       if (nr == (total - 2)) {
+                               strncpy(ref_message_id, message_id,
+                                       sizeof(ref_message_id));
+                               ref_message_id[sizeof(ref_message_id)-1]='\0';
+                               rev.ref_message_id = ref_message_id;
+                       }
+                       gen_message_id(message_id, sizeof(message_id),
+                                      sha1_to_hex(commit->object.sha1));
+                       rev.message_id = message_id;
+               }
                if (!use_stdout)
                        reopen_stdout(commit, rev.nr, keep_subject);
                shown = log_tree_commit(&rev, commit);
index 8dae9f70e2f9079d521fbc80e317addf41346d0f..79ffe8f42351f6ca5d01ce5601b6708bdfac7101 100644 (file)
@@ -322,14 +322,13 @@ static const char ls_files_usage[] =
        "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
        "[--] [<file>]*";
 
-int cmd_ls_files(int argc, const char **argv, char** envp)
+int cmd_ls_files(int argc, const char **argv, const char *prefix)
 {
        int i;
        int exc_given = 0;
        struct dir_struct dir;
 
        memset(&dir, 0, sizeof(dir));
-       prefix = setup_git_directory();
        if (prefix)
                prefix_offset = strlen(prefix);
        git_config(git_default_config);
index b8d0d88ba85d7c9770f64288971963250dc3391c..261147fdbe76cf9d277bd420cc17efedecc2da2f 100644 (file)
@@ -18,7 +18,7 @@ static int abbrev = 0;
 static int ls_options = 0;
 static const char **pathspec;
 static int chomp_prefix = 0;
-static const char *prefix;
+static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
        "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
@@ -71,7 +71,7 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
                return 0;
 
        if (chomp_prefix &&
-           (baselen < chomp_prefix || memcmp(prefix, base, chomp_prefix)))
+           (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
                return 0;
 
        if (!(ls_options & LS_NAME_ONLY))
@@ -85,13 +85,13 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
        return retval;
 }
 
-int cmd_ls_tree(int argc, const char **argv, char **envp)
+int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 {
        unsigned char sha1[20];
        struct tree *tree;
 
-       prefix = setup_git_directory();
        git_config(git_default_config);
+       ls_tree_prefix = prefix;
        if (prefix && *prefix)
                chomp_prefix = strlen(prefix);
        while (1 < argc && argv[1][0] == '-') {
index ac53f76f689fdc0a44b6e4921dc08b880e1349b0..24a4fc63b35ec4c6b5f85bda260811546b12ed46 100644 (file)
@@ -446,7 +446,7 @@ static int read_one_header_line(char *line, int sz, FILE *in)
                        break;
        }
        /* Count mbox From headers as headers */
-       if (!ofs && !memcmp(line, "From ", 5))
+       if (!ofs && (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6)))
                ofs = 1;
        return ofs;
 }
@@ -836,7 +836,7 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
 static const char mailinfo_usage[] =
        "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
 
-int cmd_mailinfo(int argc, const char **argv, char **envp)
+int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
index e2a0058435d396161145221eab80205a934b271c..91a699d34d93462b56f2591193bb5729b42b3950 100644 (file)
@@ -138,7 +138,7 @@ int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec,
        free(name);
        return ret;
 }
-int cmd_mailsplit(int argc, const char **argv, char **envp)
+int cmd_mailsplit(int argc, const char **argv, const char *prefix)
 {
        int nr = 0, nr_prec = 4, ret;
        int allow_bare = 0;
diff --git a/builtin-mv.c b/builtin-mv.c
new file mode 100644 (file)
index 0000000..62ae937
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * "git mv" builtin command
+ *
+ * Copyright (C) 2006 Johannes Schindelin
+ */
+#include <fnmatch.h>
+
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "path-list.h"
+
+static const char builtin_mv_usage[] =
+"git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)";
+
+static const char **copy_pathspec(const char *prefix, const char **pathspec,
+                                 int count, int base_name)
+{
+       const char **result = xmalloc((count + 1) * sizeof(const char *));
+       memcpy(result, pathspec, count * sizeof(const char *));
+       result[count] = NULL;
+       if (base_name) {
+               int i;
+               for (i = 0; i < count; i++) {
+                       const char *last_slash = strrchr(result[i], '/');
+                       if (last_slash)
+                               result[i] = last_slash + 1;
+               }
+       }
+       return get_pathspec(prefix, result);
+}
+
+static void show_list(const char *label, struct path_list *list)
+{
+       if (list->nr > 0) {
+               int i;
+               printf("%s", label);
+               for (i = 0; i < list->nr; i++)
+                       printf("%s%s", i > 0 ? ", " : "", list->items[i].path);
+               putchar('\n');
+       }
+}
+
+static const char *add_slash(const char *path)
+{
+       int len = strlen(path);
+       if (path[len - 1] != '/') {
+               char *with_slash = xmalloc(len + 2);
+               memcpy(with_slash, path, len);
+               strcat(with_slash + len, "/");
+               return with_slash;
+       }
+       return path;
+}
+
+static struct lock_file lock_file;
+
+int cmd_mv(int argc, const char **argv, const char *prefix)
+{
+       int i, newfd, count;
+       int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+       const char **source, **destination, **dest_path;
+       enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+       struct stat st;
+       struct path_list overwritten = {NULL, 0, 0, 0};
+       struct path_list src_for_dst = {NULL, 0, 0, 0};
+       struct path_list added = {NULL, 0, 0, 0};
+       struct path_list deleted = {NULL, 0, 0, 0};
+       struct path_list changed = {NULL, 0, 0, 0};
+
+       git_config(git_default_config);
+
+       newfd = hold_lock_file_for_update(&lock_file, get_index_file());
+       if (newfd < 0)
+               die("unable to create new index file");
+
+       if (read_cache() < 0)
+               die("index file corrupt");
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (arg[0] != '-')
+                       break;
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(arg, "-n")) {
+                       show_only = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-f")) {
+                       force = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-k")) {
+                       ignore_errors = 1;
+                       continue;
+               }
+               die(builtin_mv_usage);
+       }
+       count = argc - i - 1;
+       if (count < 1)
+               usage(builtin_mv_usage);
+
+       source = copy_pathspec(prefix, argv + i, count, 0);
+       modes = xcalloc(count, sizeof(enum update_mode));
+       dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);
+
+       if (!lstat(dest_path[0], &st) &&
+                       S_ISDIR(st.st_mode)) {
+               dest_path[0] = add_slash(dest_path[0]);
+               destination = copy_pathspec(dest_path[0], argv + i, count, 1);
+       } else {
+               if (count != 1)
+                       usage(builtin_mv_usage);
+               destination = dest_path;
+       }
+
+       /* Checking */
+       for (i = 0; i < count; i++) {
+               const char *bad = NULL;
+
+               if (show_only)
+                       printf("Checking rename of '%s' to '%s'\n",
+                               source[i], destination[i]);
+
+               if (lstat(source[i], &st) < 0)
+                       bad = "bad source";
+
+               if (S_ISDIR(st.st_mode)) {
+                       const char *dir = source[i], *dest_dir = destination[i];
+                       int first, last, len = strlen(dir);
+
+                       if (lstat(dest_dir, &st) == 0) {
+                               bad = "cannot move directory over file";
+                               goto next;
+                       }
+
+                       modes[i] = WORKING_DIRECTORY;
+
+                       first = cache_name_pos(source[i], len);
+                       if (first >= 0)
+                               die ("Huh? %s/ is in index?", dir);
+
+                       first = -1 - first;
+                       for (last = first; last < active_nr; last++) {
+                               const char *path = active_cache[last]->name;
+                               if (strncmp(path, dir, len) || path[len] != '/')
+                                       break;
+                       }
+
+                       if (last - first < 1)
+                               bad = "source directory is empty";
+                       else if (!bad) {
+                               int j, dst_len = strlen(dest_dir);
+
+                               if (last - first > 0) {
+                                       source = realloc(source,
+                                                       (count + last - first)
+                                                       * sizeof(char *));
+                                       destination = realloc(destination,
+                                                       (count + last - first)
+                                                       * sizeof(char *));
+                                       modes = realloc(modes,
+                                                       (count + last - first)
+                                                       * sizeof(enum update_mode));
+                               }
+
+                               dest_dir = add_slash(dest_dir);
+
+                               for (j = 0; j < last - first; j++) {
+                                       const char *path =
+                                               active_cache[first + j]->name;
+                                       source[count + j] = path;
+                                       destination[count + j] =
+                                               prefix_path(dest_dir, dst_len,
+                                                       path + len);
+                                       modes[count + j] = INDEX;
+                               }
+                               count += last - first;
+                       }
+
+                       goto next;
+               }
+
+               if (!bad && lstat(destination[i], &st) == 0) {
+                       bad = "destination exists";
+                       if (force) {
+                               /*
+                                * only files can overwrite each other:
+                                * check both source and destination
+                                */
+                               if (S_ISREG(st.st_mode)) {
+                                       fprintf(stderr, "Warning: %s;"
+                                                       " will overwrite!\n",
+                                                       bad);
+                                       bad = NULL;
+                                       path_list_insert(destination[i],
+                                                       &overwritten);
+                               } else
+                                       bad = "Cannot overwrite";
+                       }
+               }
+
+               if (!bad &&
+                   !strncmp(destination[i], source[i], strlen(source[i])))
+                       bad = "can not move directory into itself";
+
+               if (!bad && cache_name_pos(source[i], strlen(source[i])) < 0)
+                       bad = "not under version control";
+
+               if (!bad) {
+                       if (path_list_has_path(&src_for_dst, destination[i]))
+                               bad = "multiple sources for the same target";
+                       else
+                               path_list_insert(destination[i], &src_for_dst);
+               }
+
+next:
+               if (bad) {
+                       if (ignore_errors) {
+                               if (--count > 0) {
+                                       memmove(source + i, source + i + 1,
+                                               (count - i) * sizeof(char *));
+                                       memmove(destination + i,
+                                               destination + i + 1,
+                                               (count - i) * sizeof(char *));
+                               }
+                       } else
+                               die ("%s, source=%s, destination=%s",
+                                    bad, source[i], destination[i]);
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               if (show_only || verbose)
+                       printf("Renaming %s to %s\n",
+                              source[i], destination[i]);
+               if (!show_only && modes[i] != INDEX &&
+                   rename(source[i], destination[i]) < 0 &&
+                   !ignore_errors)
+                       die ("renaming %s failed: %s",
+                            source[i], strerror(errno));
+
+               if (modes[i] == WORKING_DIRECTORY)
+                       continue;
+
+               if (cache_name_pos(source[i], strlen(source[i])) >= 0) {
+                       path_list_insert(source[i], &deleted);
+
+                       /* destination can be a directory with 1 file inside */
+                       if (path_list_has_path(&overwritten, destination[i]))
+                               path_list_insert(destination[i], &changed);
+                       else
+                               path_list_insert(destination[i], &added);
+               } else
+                       path_list_insert(destination[i], &added);
+       }
+
+        if (show_only) {
+               show_list("Changed  : ", &changed);
+               show_list("Adding   : ", &added);
+               show_list("Deleting : ", &deleted);
+       } else {
+               for (i = 0; i < changed.nr; i++) {
+                       const char *path = changed.items[i].path;
+                       int i = cache_name_pos(path, strlen(path));
+                       struct cache_entry *ce = active_cache[i];
+
+                       if (i < 0)
+                               die ("Huh? Cache entry for %s unknown?", path);
+                       refresh_cache_entry(ce, 0);
+               }
+
+               for (i = 0; i < added.nr; i++) {
+                       const char *path = added.items[i].path;
+                       add_file_to_index(path, verbose);
+               }
+
+               for (i = 0; i < deleted.nr; i++) {
+                       const char *path = deleted.items[i].path;
+                       remove_file_from_cache(path);
+               }
+
+               if (active_cache_changed) {
+                       if (write_cache(newfd, active_cache, active_nr) ||
+                           close(newfd) ||
+                           commit_lock_file(&lock_file))
+                               die("Unable to write new index file");
+               }
+       }
+
+       return 0;
+}
index d196c41f13aee5992fe60febffa01ed4d640a97b..6a86eb52aedc45b0a59adad4da31341440f2f82e 100644 (file)
@@ -217,7 +217,7 @@ static void add_cache_refs(void)
                add_cache_tree(active_cache_tree);
 }
 
-int cmd_prune(int argc, const char **argv, char **envp)
+int cmd_prune(int argc, const char **argv, const char *prefix)
 {
        int i;
 
@@ -234,7 +234,7 @@ int cmd_prune(int argc, const char **argv, char **envp)
         * Set up revision parsing, and mark us as being interested
         * in all object types, not just commits.
         */
-       init_revisions(&revs);
+       init_revisions(&revs, prefix);
        revs.tag_objects = 1;
        revs.blob_objects = 1;
        revs.tree_objects = 1;
index 31cbfd73861326ad303472718365838329a55cb8..a824171066b8b02b82ee45030efa7e6ef359b995 100644 (file)
@@ -270,7 +270,7 @@ static int do_push(const char *repo)
        return 0;
 }
 
-int cmd_push(int argc, const char **argv, char **envp)
+int cmd_push(int argc, const char **argv, const char *prefix)
 {
        int i;
        const char *repo = "origin";    /* default repository */
index 6df5d7c5cbe2cd524cda9e0636c3ed5a8259cf33..8869cedea1f9b07d78520858876cca614283d01b 100644 (file)
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define DBRT_DEBUG 1
 
 #include "cache.h"
-
 #include "object.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
-#include <sys/time.h>
-#include <signal.h>
+#include "unpack-trees.h"
 #include "builtin.h"
 
-static int reset = 0;
-static int merge = 0;
-static int update = 0;
-static int index_only = 0;
-static int nontrivial_merge = 0;
-static int trivial_merges_only = 0;
-static int aggressive = 0;
-static int verbose_update = 0;
-static volatile int progress_update = 0;
-static const char *prefix = NULL;
-
-static int head_idx = -1;
-static int merge_size = 0;
-
 static struct object_list *trees = NULL;
 
-static struct cache_entry df_conflict_entry;
-
-struct tree_entry_list {
-       struct tree_entry_list *next;
-       unsigned directory : 1;
-       unsigned executable : 1;
-       unsigned symlink : 1;
-       unsigned int mode;
-       const char *name;
-       const unsigned char *sha1;
-};
-
-static struct tree_entry_list df_conflict_list;
-
-typedef int (*merge_fn_t)(struct cache_entry **src);
-
-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
-{
-       struct tree_desc desc;
-       struct name_entry one;
-       struct tree_entry_list *ret = NULL;
-       struct tree_entry_list **list_p = &ret;
-
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
-
-       while (tree_entry(&desc, &one)) {
-               struct tree_entry_list *entry;
-
-               entry = xmalloc(sizeof(struct tree_entry_list));
-               entry->name = one.path;
-               entry->sha1 = one.sha1;
-               entry->mode = one.mode;
-               entry->directory = S_ISDIR(one.mode) != 0;
-               entry->executable = (one.mode & S_IXUSR) != 0;
-               entry->symlink = S_ISLNK(one.mode) != 0;
-               entry->next = NULL;
-
-               *list_p = entry;
-               list_p = &entry->next;
-       }
-       return ret;
-}
-
-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
-{
-       int len1 = strlen(name1);
-       int len2 = strlen(name2);
-       int len = len1 < len2 ? len1 : len2;
-       int ret = memcmp(name1, name2, len);
-       unsigned char c1, c2;
-       if (ret)
-               return ret;
-       c1 = name1[len];
-       c2 = name2[len];
-       if (!c1 && dir1)
-               c1 = '/';
-       if (!c2 && dir2)
-               c2 = '/';
-       ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-       if (c1 && c2 && !ret)
-               ret = len1 - len2;
-       return ret;
-}
-
-static int unpack_trees_rec(struct tree_entry_list **posns, int len,
-                           const char *base, merge_fn_t fn, int *indpos)
-{
-       int baselen = strlen(base);
-       int src_size = len + 1;
-       do {
-               int i;
-               const char *first;
-               int firstdir = 0;
-               int pathlen;
-               unsigned ce_size;
-               struct tree_entry_list **subposns;
-               struct cache_entry **src;
-               int any_files = 0;
-               int any_dirs = 0;
-               char *cache_name;
-               int ce_stage;
-
-               /* Find the first name in the input. */
-
-               first = NULL;
-               cache_name = NULL;
-
-               /* Check the cache */
-               if (merge && *indpos < active_nr) {
-                       /* This is a bit tricky: */
-                       /* If the index has a subdirectory (with
-                        * contents) as the first name, it'll get a
-                        * filename like "foo/bar". But that's after
-                        * "foo", so the entry in trees will get
-                        * handled first, at which point we'll go into
-                        * "foo", and deal with "bar" from the index,
-                        * because the base will be "foo/". The only
-                        * way we can actually have "foo/bar" first of
-                        * all the things is if the trees don't
-                        * contain "foo" at all, in which case we'll
-                        * handle "foo/bar" without going into the
-                        * directory, but that's fine (and will return
-                        * an error anyway, with the added unknown
-                        * file case.
-                        */
-
-                       cache_name = active_cache[*indpos]->name;
-                       if (strlen(cache_name) > baselen &&
-                           !memcmp(cache_name, base, baselen)) {
-                               cache_name += baselen;
-                               first = cache_name;
-                       } else {
-                               cache_name = NULL;
-                       }
-               }
-
-#if DBRT_DEBUG > 1
-               if (first)
-                       printf("index %s\n", first);
-#endif
-               for (i = 0; i < len; i++) {
-                       if (!posns[i] || posns[i] == &df_conflict_list)
-                               continue;
-#if DBRT_DEBUG > 1
-                       printf("%d %s\n", i + 1, posns[i]->name);
-#endif
-                       if (!first || entcmp(first, firstdir,
-                                            posns[i]->name, 
-                                            posns[i]->directory) > 0) {
-                               first = posns[i]->name;
-                               firstdir = posns[i]->directory;
-                       }
-               }
-               /* No name means we're done */
-               if (!first)
-                       return 0;
-
-               pathlen = strlen(first);
-               ce_size = cache_entry_size(baselen + pathlen);
-
-               src = xcalloc(src_size, sizeof(struct cache_entry *));
-
-               subposns = xcalloc(len, sizeof(struct tree_list_entry *));
-
-               if (cache_name && !strcmp(cache_name, first)) {
-                       any_files = 1;
-                       src[0] = active_cache[*indpos];
-                       remove_cache_entry_at(*indpos);
-               }
-
-               for (i = 0; i < len; i++) {
-                       struct cache_entry *ce;
-
-                       if (!posns[i] ||
-                           (posns[i] != &df_conflict_list &&
-                            strcmp(first, posns[i]->name))) {
-                               continue;
-                       }
-
-                       if (posns[i] == &df_conflict_list) {
-                               src[i + merge] = &df_conflict_entry;
-                               continue;
-                       }
-
-                       if (posns[i]->directory) {
-                               struct tree *tree = lookup_tree(posns[i]->sha1);
-                               any_dirs = 1;
-                               parse_tree(tree);
-                               subposns[i] = create_tree_entry_list(tree);
-                               posns[i] = posns[i]->next;
-                               src[i + merge] = &df_conflict_entry;
-                               continue;
-                       }
-
-                       if (!merge)
-                               ce_stage = 0;
-                       else if (i + 1 < head_idx)
-                               ce_stage = 1;
-                       else if (i + 1 > head_idx)
-                               ce_stage = 3;
-                       else
-                               ce_stage = 2;
-
-                       ce = xcalloc(1, ce_size);
-                       ce->ce_mode = create_ce_mode(posns[i]->mode);
-                       ce->ce_flags = create_ce_flags(baselen + pathlen,
-                                                      ce_stage);
-                       memcpy(ce->name, base, baselen);
-                       memcpy(ce->name + baselen, first, pathlen + 1);
-
-                       any_files = 1;
-
-                       memcpy(ce->sha1, posns[i]->sha1, 20);
-                       src[i + merge] = ce;
-                       subposns[i] = &df_conflict_list;
-                       posns[i] = posns[i]->next;
-               }
-               if (any_files) {
-                       if (merge) {
-                               int ret;
-
-#if DBRT_DEBUG > 1
-                               printf("%s:\n", first);
-                               for (i = 0; i < src_size; i++) {
-                                       printf(" %d ", i);
-                                       if (src[i])
-                                               printf("%s\n", sha1_to_hex(src[i]->sha1));
-                                       else
-                                               printf("\n");
-                               }
-#endif
-                               ret = fn(src);
-                               
-#if DBRT_DEBUG > 1
-                               printf("Added %d entries\n", ret);
-#endif
-                               *indpos += ret;
-                       } else {
-                               for (i = 0; i < src_size; i++) {
-                                       if (src[i]) {
-                                               add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-                                       }
-                               }
-                       }
-               }
-               if (any_dirs) {
-                       char *newbase = xmalloc(baselen + 2 + pathlen);
-                       memcpy(newbase, base, baselen);
-                       memcpy(newbase + baselen, first, pathlen);
-                       newbase[baselen + pathlen] = '/';
-                       newbase[baselen + pathlen + 1] = '\0';
-                       if (unpack_trees_rec(subposns, len, newbase, fn,
-                                            indpos))
-                               return -1;
-                       free(newbase);
-               }
-               free(subposns);
-               free(src);
-       } while (1);
-}
-
-static void reject_merge(struct cache_entry *ce)
-{
-       die("Entry '%s' would be overwritten by merge. Cannot merge.", 
-           ce->name);
-}
-
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
- */
-static void unlink_entry(char *name)
-{
-       char *cp, *prev;
-
-       if (unlink(name))
-               return;
-       prev = NULL;
-       while (1) {
-               int status;
-               cp = strrchr(name, '/');
-               if (prev)
-                       *prev = '/';
-               if (!cp)
-                       break;
-
-               *cp = 0;
-               status = rmdir(name);
-               if (status) {
-                       *cp = '/';
-                       break;
-               }
-               prev = cp;
-       }
-}
-
-static void progress_interval(int signum)
-{
-       progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
-       struct sigaction sa;
-       struct itimerval v;
-
-       memset(&sa, 0, sizeof(sa));
-       sa.sa_handler = progress_interval;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_flags = SA_RESTART;
-       sigaction(SIGALRM, &sa, NULL);
-
-       v.it_interval.tv_sec = 1;
-       v.it_interval.tv_usec = 0;
-       v.it_value = v.it_interval;
-       setitimer(ITIMER_REAL, &v, NULL);
-}
-
-static struct checkout state;
-static void check_updates(struct cache_entry **src, int nr)
-{
-       unsigned short mask = htons(CE_UPDATE);
-       unsigned last_percent = 200, cnt = 0, total = 0;
-
-       if (update && verbose_update) {
-               for (total = cnt = 0; cnt < nr; cnt++) {
-                       struct cache_entry *ce = src[cnt];
-                       if (!ce->ce_mode || ce->ce_flags & mask)
-                               total++;
-               }
-
-               /* Don't bother doing this for very small updates */
-               if (total < 250)
-                       total = 0;
-
-               if (total) {
-                       fprintf(stderr, "Checking files out...\n");
-                       setup_progress_signal();
-                       progress_update = 1;
-               }
-               cnt = 0;
-       }
-
-       while (nr--) {
-               struct cache_entry *ce = *src++;
-
-               if (total) {
-                       if (!ce->ce_mode || ce->ce_flags & mask) {
-                               unsigned percent;
-                               cnt++;
-                               percent = (cnt * 100) / total;
-                               if (percent != last_percent ||
-                                   progress_update) {
-                                       fprintf(stderr, "%4u%% (%u/%u) done\r",
-                                               percent, cnt, total);
-                                       last_percent = percent;
-                                       progress_update = 0;
-                               }
-                       }
-               }
-               if (!ce->ce_mode) {
-                       if (update)
-                               unlink_entry(ce->name);
-                       continue;
-               }
-               if (ce->ce_flags & mask) {
-                       ce->ce_flags &= ~mask;
-                       if (update)
-                               checkout_entry(ce, &state, NULL);
-               }
-       }
-       if (total) {
-               signal(SIGALRM, SIG_IGN);
-               fputc('\n', stderr);
-       }
-}
-
-static int unpack_trees(merge_fn_t fn)
-{
-       int indpos = 0;
-       unsigned len = object_list_length(trees);
-       struct tree_entry_list **posns;
-       int i;
-       struct object_list *posn = trees;
-       merge_size = len;
-
-       if (len) {
-               posns = xmalloc(len * sizeof(struct tree_entry_list *));
-               for (i = 0; i < len; i++) {
-                       posns[i] = create_tree_entry_list((struct tree *) posn->item);
-                       posn = posn->next;
-               }
-               if (unpack_trees_rec(posns, len, prefix ? prefix : "",
-                                    fn, &indpos))
-                       return -1;
-       }
-
-       if (trivial_merges_only && nontrivial_merge)
-               die("Merge requires file-level merging");
-
-       check_updates(active_cache, active_nr);
-       return 0;
-}
-
 static int list_tree(unsigned char *sha1)
 {
        struct tree *tree = parse_tree_indirect(sha1);
@@ -425,386 +23,6 @@ static int list_tree(unsigned char *sha1)
        return 0;
 }
 
-static int same(struct cache_entry *a, struct cache_entry *b)
-{
-       if (!!a != !!b)
-               return 0;
-       if (!a && !b)
-               return 1;
-       return a->ce_mode == b->ce_mode && 
-               !memcmp(a->sha1, b->sha1, 20);
-}
-
-
-/*
- * When a CE gets turned into an unmerged entry, we
- * want it to be up-to-date
- */
-static void verify_uptodate(struct cache_entry *ce)
-{
-       struct stat st;
-
-       if (index_only || reset)
-               return;
-
-       if (!lstat(ce->name, &st)) {
-               unsigned changed = ce_match_stat(ce, &st, 1);
-               if (!changed)
-                       return;
-               errno = 0;
-       }
-       if (reset) {
-               ce->ce_flags |= htons(CE_UPDATE);
-               return;
-       }
-       if (errno == ENOENT)
-               return;
-       die("Entry '%s' not uptodate. Cannot merge.", ce->name);
-}
-
-static void invalidate_ce_path(struct cache_entry *ce)
-{
-       if (ce)
-               cache_tree_invalidate_path(active_cache_tree, ce->name);
-}
-
-/*
- * We do not want to remove or overwrite a working tree file that
- * is not tracked.
- */
-static void verify_absent(const char *path, const char *action)
-{
-       struct stat st;
-
-       if (index_only || reset || !update)
-               return;
-       if (!lstat(path, &st))
-               die("Untracked working tree file '%s' "
-                   "would be %s by merge.", path, action);
-}
-
-static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
-{
-       merge->ce_flags |= htons(CE_UPDATE);
-       if (old) {
-               /*
-                * See if we can re-use the old CE directly?
-                * That way we get the uptodate stat info.
-                *
-                * This also removes the UPDATE flag on
-                * a match.
-                */
-               if (same(old, merge)) {
-                       *merge = *old;
-               } else {
-                       verify_uptodate(old);
-                       invalidate_ce_path(old);
-               }
-       }
-       else {
-               verify_absent(merge->name, "overwritten");
-               invalidate_ce_path(merge);
-       }
-
-       merge->ce_flags &= ~htons(CE_STAGEMASK);
-       add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
-       return 1;
-}
-
-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
-{
-       if (old)
-               verify_uptodate(old);
-       else
-               verify_absent(ce->name, "removed");
-       ce->ce_mode = 0;
-       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
-       invalidate_ce_path(ce);
-       return 1;
-}
-
-static int keep_entry(struct cache_entry *ce)
-{
-       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
-       return 1;
-}
-
-#if DBRT_DEBUG
-static void show_stage_entry(FILE *o,
-                            const char *label, const struct cache_entry *ce)
-{
-       if (!ce)
-               fprintf(o, "%s (missing)\n", label);
-       else
-               fprintf(o, "%s%06o %s %d\t%s\n",
-                       label,
-                       ntohl(ce->ce_mode),
-                       sha1_to_hex(ce->sha1),
-                       ce_stage(ce),
-                       ce->name);
-}
-#endif
-
-static int threeway_merge(struct cache_entry **stages)
-{
-       struct cache_entry *index;
-       struct cache_entry *head; 
-       struct cache_entry *remote = stages[head_idx + 1];
-       int count;
-       int head_match = 0;
-       int remote_match = 0;
-       const char *path = NULL;
-
-       int df_conflict_head = 0;
-       int df_conflict_remote = 0;
-
-       int any_anc_missing = 0;
-       int no_anc_exists = 1;
-       int i;
-
-       for (i = 1; i < head_idx; i++) {
-               if (!stages[i])
-                       any_anc_missing = 1;
-               else {
-                       if (!path)
-                               path = stages[i]->name;
-                       no_anc_exists = 0;
-               }
-       }
-
-       index = stages[0];
-       head = stages[head_idx];
-
-       if (head == &df_conflict_entry) {
-               df_conflict_head = 1;
-               head = NULL;
-       }
-
-       if (remote == &df_conflict_entry) {
-               df_conflict_remote = 1;
-               remote = NULL;
-       }
-
-       if (!path && index)
-               path = index->name;
-       if (!path && head)
-               path = head->name;
-       if (!path && remote)
-               path = remote->name;
-
-       /* First, if there's a #16 situation, note that to prevent #13
-        * and #14.
-        */
-       if (!same(remote, head)) {
-               for (i = 1; i < head_idx; i++) {
-                       if (same(stages[i], head)) {
-                               head_match = i;
-                       }
-                       if (same(stages[i], remote)) {
-                               remote_match = i;
-                       }
-               }
-       }
-
-       /* We start with cases where the index is allowed to match
-        * something other than the head: #14(ALT) and #2ALT, where it
-        * is permitted to match the result instead.
-        */
-       /* #14, #14ALT, #2ALT */
-       if (remote && !df_conflict_head && head_match && !remote_match) {
-               if (index && !same(index, remote) && !same(index, head))
-                       reject_merge(index);
-               return merged_entry(remote, index);
-       }
-       /*
-        * If we have an entry in the index cache, then we want to
-        * make sure that it matches head.
-        */
-       if (index && !same(index, head)) {
-               reject_merge(index);
-       }
-
-       if (head) {
-               /* #5ALT, #15 */
-               if (same(head, remote))
-                       return merged_entry(head, index);
-               /* #13, #3ALT */
-               if (!df_conflict_remote && remote_match && !head_match)
-                       return merged_entry(head, index);
-       }
-
-       /* #1 */
-       if (!head && !remote && any_anc_missing)
-               return 0;
-
-       /* Under the new "aggressive" rule, we resolve mostly trivial
-        * cases that we historically had git-merge-one-file resolve.
-        */
-       if (aggressive) {
-               int head_deleted = !head && !df_conflict_head;
-               int remote_deleted = !remote && !df_conflict_remote;
-               /*
-                * Deleted in both.
-                * Deleted in one and unchanged in the other.
-                */
-               if ((head_deleted && remote_deleted) ||
-                   (head_deleted && remote && remote_match) ||
-                   (remote_deleted && head && head_match)) {
-                       if (index)
-                               return deleted_entry(index, index);
-                       else if (path)
-                               verify_absent(path, "removed");
-                       return 0;
-               }
-               /*
-                * Added in both, identically.
-                */
-               if (no_anc_exists && head && remote && same(head, remote))
-                       return merged_entry(head, index);
-
-       }
-
-       /* Below are "no merge" cases, which require that the index be
-        * up-to-date to avoid the files getting overwritten with
-        * conflict resolution files. 
-        */
-       if (index) {
-               verify_uptodate(index);
-       }
-       else if (path)
-               verify_absent(path, "overwritten");
-
-       nontrivial_merge = 1;
-
-       /* #2, #3, #4, #6, #7, #9, #11. */
-       count = 0;
-       if (!head_match || !remote_match) {
-               for (i = 1; i < head_idx; i++) {
-                       if (stages[i]) {
-                               keep_entry(stages[i]);
-                               count++;
-                               break;
-                       }
-               }
-       }
-#if DBRT_DEBUG
-       else {
-               fprintf(stderr, "read-tree: warning #16 detected\n");
-               show_stage_entry(stderr, "head   ", stages[head_match]);
-               show_stage_entry(stderr, "remote ", stages[remote_match]);
-       }
-#endif
-       if (head) { count += keep_entry(head); }
-       if (remote) { count += keep_entry(remote); }
-       return count;
-}
-
-/*
- * Two-way merge.
- *
- * The rule is to "carry forward" what is in the index without losing
- * information across a "fast forward", favoring a successful merge
- * over a merge failure when it makes sense.  For details of the
- * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
- *
- */
-static int twoway_merge(struct cache_entry **src)
-{
-       struct cache_entry *current = src[0];
-       struct cache_entry *oldtree = src[1], *newtree = src[2];
-
-       if (merge_size != 2)
-               return error("Cannot do a twoway merge of %d trees",
-                            merge_size);
-
-       if (current) {
-               if ((!oldtree && !newtree) || /* 4 and 5 */
-                   (!oldtree && newtree &&
-                    same(current, newtree)) || /* 6 and 7 */
-                   (oldtree && newtree &&
-                    same(oldtree, newtree)) || /* 14 and 15 */
-                   (oldtree && newtree &&
-                    !same(oldtree, newtree) && /* 18 and 19*/
-                    same(current, newtree))) {
-                       return keep_entry(current);
-               }
-               else if (oldtree && !newtree && same(current, oldtree)) {
-                       /* 10 or 11 */
-                       return deleted_entry(oldtree, current);
-               }
-               else if (oldtree && newtree &&
-                        same(current, oldtree) && !same(current, newtree)) {
-                       /* 20 or 21 */
-                       return merged_entry(newtree, current);
-               }
-               else {
-                       /* all other failures */
-                       if (oldtree)
-                               reject_merge(oldtree);
-                       if (current)
-                               reject_merge(current);
-                       if (newtree)
-                               reject_merge(newtree);
-                       return -1;
-               }
-       }
-       else if (newtree)
-               return merged_entry(newtree, current);
-       else
-               return deleted_entry(oldtree, current);
-}
-
-/*
- * Bind merge.
- *
- * Keep the index entries at stage0, collapse stage1 but make sure
- * stage0 does not have anything there.
- */
-static int bind_merge(struct cache_entry **src)
-{
-       struct cache_entry *old = src[0];
-       struct cache_entry *a = src[1];
-
-       if (merge_size != 1)
-               return error("Cannot do a bind merge of %d trees\n",
-                            merge_size);
-       if (a && old)
-               die("Entry '%s' overlaps.  Cannot bind.", a->name);
-       if (!a)
-               return keep_entry(old);
-       else
-               return merged_entry(a, NULL);
-}
-
-/*
- * One-way merge.
- *
- * The rule is:
- * - take the stat information from stage0, take the data from stage1
- */
-static int oneway_merge(struct cache_entry **src)
-{
-       struct cache_entry *old = src[0];
-       struct cache_entry *a = src[1];
-
-       if (merge_size != 1)
-               return error("Cannot do a oneway merge of %d trees",
-                            merge_size);
-
-       if (!a)
-               return deleted_entry(old, old);
-       if (old && same(old, a)) {
-               if (reset) {
-                       struct stat st;
-                       if (lstat(old->name, &st) ||
-                           ce_match_stat(old, &st, 1))
-                               old->ce_flags |= htons(CE_UPDATE);
-               }
-               return keep_entry(old);
-       }
-       return merged_entry(a, old);
-}
-
 static int read_cache_unmerged(void)
 {
        int i;
@@ -818,7 +36,7 @@ static int read_cache_unmerged(void)
                if (ce_stage(ce)) {
                        if (last && !strcmp(ce->name, last->name))
                                continue;
-                       invalidate_ce_path(ce);
+                       cache_tree_invalidate_path(active_cache_tree, ce->name);
                        last = ce;
                        ce->ce_mode = 0;
                        ce->ce_flags &= ~htons(CE_STAGEMASK);
@@ -870,17 +88,14 @@ static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive
 
 static struct lock_file lock_file;
 
-int cmd_read_tree(int argc, const char **argv, char **envp)
+int cmd_read_tree(int argc, const char **argv, const char *prefix)
 {
        int i, newfd, stage = 0;
        unsigned char sha1[20];
-       merge_fn_t fn = NULL;
+       struct unpack_trees_options opts;
 
-       df_conflict_list.next = &df_conflict_list;
-       state.base_dir = "";
-       state.force = 1;
-       state.quiet = 1;
-       state.refresh_cache = 1;
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = -1;
 
        setup_git_directory();
        git_config(git_default_config);
@@ -891,8 +106,6 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
 
        git_config(git_default_config);
 
-       merge = 0;
-       reset = 0;
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
@@ -900,12 +113,12 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                 * the working tree.
                 */
                if (!strcmp(arg, "-u")) {
-                       update = 1;
+                       opts.update = 1;
                        continue;
                }
 
                if (!strcmp(arg, "-v")) {
-                       verbose_update = 1;
+                       opts.verbose_update = 1;
                        continue;
                }
 
@@ -913,7 +126,7 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                 * not even look at the working tree.
                 */
                if (!strcmp(arg, "-i")) {
-                       index_only = 1;
+                       opts.index_only = 1;
                        continue;
                }
 
@@ -922,10 +135,10 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                 * given subdirectory.
                 */
                if (!strncmp(arg, "--prefix=", 9)) {
-                       if (stage || merge || prefix)
+                       if (stage || opts.merge || opts.prefix)
                                usage(read_tree_usage);
-                       prefix = arg + 9;
-                       merge = 1;
+                       opts.prefix = arg + 9;
+                       opts.merge = 1;
                        stage = 1;
                        if (read_cache_unmerged())
                                die("you need to resolve your current index first");
@@ -937,38 +150,38 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                 * correspond to them.
                 */
                if (!strcmp(arg, "--reset")) {
-                       if (stage || merge || prefix)
+                       if (stage || opts.merge || opts.prefix)
                                usage(read_tree_usage);
-                       reset = 1;
-                       merge = 1;
+                       opts.reset = 1;
+                       opts.merge = 1;
                        stage = 1;
                        read_cache_unmerged();
                        continue;
                }
 
                if (!strcmp(arg, "--trivial")) {
-                       trivial_merges_only = 1;
+                       opts.trivial_merges_only = 1;
                        continue;
                }
 
                if (!strcmp(arg, "--aggressive")) {
-                       aggressive = 1;
+                       opts.aggressive = 1;
                        continue;
                }
 
                /* "-m" stands for "merge", meaning we start in stage 1 */
                if (!strcmp(arg, "-m")) {
-                       if (stage || merge || prefix)
+                       if (stage || opts.merge || opts.prefix)
                                usage(read_tree_usage);
                        if (read_cache_unmerged())
                                die("you need to resolve your current index first");
                        stage = 1;
-                       merge = 1;
+                       opts.merge = 1;
                        continue;
                }
 
                /* using -u and -i at the same time makes no sense */
-               if (1 < index_only + update)
+               if (1 < opts.index_only + opts.update)
                        usage(read_tree_usage);
 
                if (get_sha1(arg, sha1))
@@ -977,52 +190,53 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                        die("failed to unpack tree object %s", arg);
                stage++;
        }
-       if ((update||index_only) && !merge)
+       if ((opts.update||opts.index_only) && !opts.merge)
                usage(read_tree_usage);
 
-       if (prefix) {
-               int pfxlen = strlen(prefix);
+       if (opts.prefix) {
+               int pfxlen = strlen(opts.prefix);
                int pos;
-               if (prefix[pfxlen-1] != '/')
+               if (opts.prefix[pfxlen-1] != '/')
                        die("prefix must end with /");
                if (stage != 2)
                        die("binding merge takes only one tree");
-               pos = cache_name_pos(prefix, pfxlen);
+               pos = cache_name_pos(opts.prefix, pfxlen);
                if (0 <= pos)
                        die("corrupt index file");
                pos = -pos-1;
                if (pos < active_nr &&
-                   !strncmp(active_cache[pos]->name, prefix, pfxlen))
-                       die("subdirectory '%s' already exists.", prefix);
-               pos = cache_name_pos(prefix, pfxlen-1);
+                   !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
+                       die("subdirectory '%s' already exists.", opts.prefix);
+               pos = cache_name_pos(opts.prefix, pfxlen-1);
                if (0 <= pos)
-                       die("file '%.*s' already exists.", pfxlen-1, prefix);
+                       die("file '%.*s' already exists.",
+                                       pfxlen-1, opts.prefix);
        }
 
-       if (merge) {
+       if (opts.merge) {
                if (stage < 2)
                        die("just how do you expect me to merge %d trees?", stage-1);
                switch (stage - 1) {
                case 1:
-                       fn = prefix ? bind_merge : oneway_merge;
+                       opts.fn = opts.prefix ? bind_merge : oneway_merge;
                        break;
                case 2:
-                       fn = twoway_merge;
+                       opts.fn = twoway_merge;
                        break;
                case 3:
                default:
-                       fn = threeway_merge;
+                       opts.fn = threeway_merge;
                        cache_tree_free(&active_cache_tree);
                        break;
                }
 
                if (stage - 1 >= 3)
-                       head_idx = stage - 2;
+                       opts.head_idx = stage - 2;
                else
-                       head_idx = 1;
+                       opts.head_idx = 1;
        }
 
-       unpack_trees(fn);
+       unpack_trees(trees, &opts);
 
        /*
         * When reading only one tree (either the most basic form,
@@ -1030,7 +244,7 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
         * valid cache-tree because the index must match exactly
         * what came from the tree.
         */
-       if (trees && trees->item && !prefix && (!merge || (stage == 2))) {
+       if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {
                cache_tree_free(&active_cache_tree);
                prime_cache_tree();
        }
index 8f328713374679b023f1f8caaacab592c9e4f9a2..0dee1734a31308ace74b0b25cc87ffcf4993549b 100644 (file)
@@ -306,12 +306,12 @@ static void mark_edges_uninteresting(struct commit_list *list)
        }
 }
 
-int cmd_rev_list(int argc, const char **argv, char **envp)
+int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
        struct commit_list *list;
        int i;
 
-       init_revisions(&revs);
+       init_revisions(&revs, prefix);
        revs.abbrev = 0;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
        argc = setup_revisions(argc, argv, &revs, NULL);
index b3e4386c1baec7c3a56704e61e44e575203ab759..aca4a3603283a257f7621934103c85d5c0c31390 100644 (file)
@@ -209,11 +209,10 @@ static int try_difference(const char *arg)
        return 0;
 }
 
-int cmd_rev_parse(int argc, const char **argv, char **envp)
+int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0;
        unsigned char sha1[20];
-       const char *prefix = setup_git_directory();
 
        git_config(git_default_config);
 
index 5deb81171996029b31da14109c408ffba33f7957..92d205a7153a7cc5267ea9cf774bc1e4fac887d4 100644 (file)
@@ -43,11 +43,10 @@ static int remove_file(const char *name)
 
 static struct lock_file lock_file;
 
-int cmd_rm(int argc, const char **argv, char **envp)
+int cmd_rm(int argc, const char **argv, const char *prefix)
 {
        int i, newfd;
        int verbose = 0, show_only = 0, force = 0;
-       const char *prefix = setup_git_directory();
        const char **pathspec;
        char *seen;
 
@@ -90,8 +89,7 @@ int cmd_rm(int argc, const char **argv, char **envp)
        seen = NULL;
        for (i = 0; pathspec[i] ; i++)
                /* nothing */;
-       seen = xmalloc(i);
-       memset(seen, 0, i);
+       seen = xcalloc(i, 1);
 
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
index 260cb221b902aa05d389f79988434770ed8c9131..2a1b848f6c169f09cb168d12e6bb5033795b8be2 100644 (file)
@@ -89,6 +89,8 @@ static int name_first_parent_chain(struct commit *c)
                        name_parent(c, p);
                        i++;
                }
+               else
+                       break;
                c = p;
        }
        return i;
@@ -172,7 +174,7 @@ static void name_commits(struct commit_list *list,
 static int mark_seen(struct commit *commit, struct commit_list **seen_p)
 {
        if (!commit->object.flags) {
-               insert_by_date(commit, seen_p);
+               commit_list_insert(commit, seen_p);
                return 1;
        }
        return 0;
@@ -218,9 +220,8 @@ static void join_revs(struct commit_list **list_p,
         * Postprocess to complete well-poisoning.
         *
         * At this point we have all the commits we have seen in
-        * seen_p list (which happens to be sorted chronologically but
-        * it does not really matter).  Mark anything that can be
-        * reached from uninteresting commits not interesting.
+        * seen_p list.  Mark anything that can be reached from
+        * uninteresting commits not interesting.
         */
        for (;;) {
                int changed = 0;
@@ -549,7 +550,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
        return 0;
 }
 
-int cmd_show_branch(int ac, const char **av, char **envp)
+int cmd_show_branch(int ac, const char **av, const char *prefix)
 {
        struct commit *rev[MAX_REVS], *commit;
        struct commit_list *list = NULL, *seen = NULL;
@@ -572,7 +573,6 @@ int cmd_show_branch(int ac, const char **av, char **envp)
        int topics = 0;
        int dense = 1;
 
-       setup_git_directory();
        git_config(git_show_branch_config);
 
        /* If nothing is specified, try the default first */
@@ -701,6 +701,8 @@ int cmd_show_branch(int ac, const char **av, char **envp)
        if (0 <= extra)
                join_revs(&list, &seen, num_rev, extra);
 
+       sort_by_date(&seen);
+
        if (merge_base)
                return show_merge_base(seen, num_rev);
 
index 2ce1264f7b4bfb5baf85f82907e2a8f7219846fa..09cc9108cdaf2aa20dc10788bccd2fee0ae9b02a 100644 (file)
@@ -54,7 +54,7 @@ void stripspace(FILE *in, FILE *out)
                fputc('\n', out);
 }
 
-int cmd_stripspace(int argc, const char **argv, char **envp)
+int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
        stripspace(stdin, stdout);
        return 0;
index f2e48aae2a04822d26d255dddf36002f58f2093c..7c48db9ec85adb1b8866b2cc1dc0eccd2d1816c7 100644 (file)
@@ -20,6 +20,7 @@ static char block[BLOCKSIZE];
 static unsigned long offset;
 
 static time_t archive_time;
+static int tar_umask;
 
 /* tries hard to write, either succeeds or dies in the attempt */
 static void reliable_write(const void *data, unsigned long size)
@@ -188,13 +189,13 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
        } else {
                if (S_ISDIR(mode)) {
                        *header.typeflag = TYPEFLAG_DIR;
-                       mode |= 0777;
+                       mode = (mode | 0777) & ~tar_umask;
                } else if (S_ISLNK(mode)) {
                        *header.typeflag = TYPEFLAG_LNK;
                        mode |= 0777;
                } else if (S_ISREG(mode)) {
                        *header.typeflag = TYPEFLAG_REG;
-                       mode |= (mode & 0100) ? 0777 : 0666;
+                       mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
                } else {
                        error("unsupported file mode: 0%o (SHA1: %s)",
                              mode, sha1_to_hex(sha1));
@@ -293,7 +294,21 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
        }
 }
 
-static int generate_tar(int argc, const char **argv, char** envp)
+int git_tar_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "tar.umask")) {
+               if (!strcmp(value, "user")) {
+                       tar_umask = umask(0);
+                       umask(tar_umask);
+               } else {
+                       tar_umask = git_config_int(var, value);
+               }
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+static int generate_tar(int argc, const char **argv, const char *prefix)
 {
        unsigned char sha1[20], tree_sha1[20];
        struct commit *commit;
@@ -304,8 +319,7 @@ static int generate_tar(int argc, const char **argv, char** envp)
        current_path.alloc = PATH_MAX;
        current_path.len = current_path.eof = 0;
 
-       setup_git_directory();
-       git_config(git_default_config);
+       git_config(git_tar_config);
 
        switch (argc) {
        case 3:
@@ -387,19 +401,19 @@ static int remote_tar(int argc, const char **argv)
        return !!ret;
 }
 
-int cmd_tar_tree(int argc, const char **argv, char **envp)
+int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
        if (argc < 2)
                usage(tar_tree_usage);
        if (!strncmp("--remote=", argv[1], 9))
                return remote_tar(argc, argv);
-       return generate_tar(argc, argv, envp);
+       return generate_tar(argc, argv, prefix);
 }
 
 /* ustar header + extended global header content */
 #define HEADERSIZE (2 * RECORDSIZE)
 
-int cmd_get_tar_commit_id(int argc, const char **argv, char **envp)
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
 {
        char buffer[HEADERSIZE];
        struct ustar_header *header = (struct ustar_header *)buffer;
index 1a4200d151dd8e876c6f84531422cae50b049aa3..24dca47d8d6e54429aa89b04e4d67bda04fa0173 100644 (file)
@@ -476,12 +476,11 @@ static int do_reupdate(int ac, const char **av,
        return 0;
 }
 
-int cmd_update_index(int argc, const char **argv, char **envp)
+int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
        int i, newfd, entries, has_errors = 0, line_termination = '\n';
        int allow_options = 1;
        int read_from_stdin = 0;
-       const char *prefix = setup_git_directory();
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
        unsigned int refresh_flags = 0;
index 83094abe0f20d36bf2d9e65539fb2c8b7a5644fc..5bd71825fdc48392952faf170efe98c072c7e482 100644 (file)
@@ -5,7 +5,7 @@
 static const char git_update_ref_usage[] =
 "git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
 
-int cmd_update_ref(int argc, const char **argv, char **envp)
+int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
        const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
        struct ref_lock *lock;
@@ -13,7 +13,6 @@ int cmd_update_ref(int argc, const char **argv, char **envp)
        int i;
 
        setup_ident();
-       setup_git_directory();
        git_config(git_default_config);
 
        for (i = 1; i < argc; i++) {
index d4fa7b56c39f03e8b48d513f967d89d4031bacb2..7b401bbb779ea0cc8bca3720eab612af2b50a4c3 100644 (file)
@@ -15,7 +15,7 @@ static int nak(const char *reason)
        return 1;
 }
 
-int cmd_upload_tar(int argc, const char **argv, char **envp)
+int cmd_upload_tar(int argc, const char **argv, const char *prefix)
 {
        int len;
        const char *dir = argv[1];
index 449a4d1b575b591aa07a40532649f9ea06eb6890..0289f59936267194ccbb8276a525ad24198a5de9 100644 (file)
@@ -60,14 +60,12 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
        return 0;
 }
 
-int cmd_write_tree(int argc, const char **argv, char **envp)
+int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
 {
        int missing_ok = 0, ret;
        const char *prefix = NULL;
        unsigned char sha1[20];
 
-       setup_git_directory();
-
        while (1 < argc) {
                const char *arg = argv[1];
                if (!strcmp(arg, "--missing-ok"))
index 5339d8627f48a44e3fbf3c81a0790bc467ccd459..1c8637ae22dc46961772b690d4218933725565d4 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -15,53 +15,54 @@ void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
 #endif
        ;
 
-extern int cmd_help(int argc, const char **argv, char **envp);
-extern int cmd_version(int argc, const char **argv, char **envp);
+extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_version(int argc, const char **argv, const char *prefix);
 
-extern int cmd_whatchanged(int argc, const char **argv, char **envp);
-extern int cmd_show(int argc, const char **argv, char **envp);
-extern int cmd_log(int argc, const char **argv, char **envp);
-extern int cmd_diff(int argc, const char **argv, char **envp);
-extern int cmd_format_patch(int argc, const char **argv, char **envp);
-extern int cmd_count_objects(int argc, const char **argv, char **envp);
+extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
+extern int cmd_show(int argc, const char **argv, const char *prefix);
+extern int cmd_log(int argc, const char **argv, const char *prefix);
+extern int cmd_diff(int argc, const char **argv, const char *prefix);
+extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
+extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 
-extern int cmd_prune(int argc, const char **argv, char **envp);
+extern int cmd_prune(int argc, const char **argv, const char *prefix);
 
-extern int cmd_push(int argc, const char **argv, char **envp);
-extern int cmd_grep(int argc, const char **argv, char **envp);
-extern int cmd_rm(int argc, const char **argv, char **envp);
-extern int cmd_add(int argc, const char **argv, char **envp);
-extern int cmd_rev_list(int argc, const char **argv, char **envp);
-extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
-extern int cmd_init_db(int argc, const char **argv, char **envp);
-extern int cmd_tar_tree(int argc, const char **argv, char **envp);
-extern int cmd_upload_tar(int argc, const char **argv, char **envp);
-extern int cmd_get_tar_commit_id(int argc, const char **argv, char **envp);
-extern int cmd_ls_files(int argc, const char **argv, char **envp);
-extern int cmd_ls_tree(int argc, const char **argv, char **envp);
-extern int cmd_read_tree(int argc, const char **argv, char **envp);
-extern int cmd_commit_tree(int argc, const char **argv, char **envp);
-extern int cmd_apply(int argc, const char **argv, char **envp);
-extern int cmd_show_branch(int argc, const char **argv, char **envp);
-extern int cmd_diff_files(int argc, const char **argv, char **envp);
-extern int cmd_diff_index(int argc, const char **argv, char **envp);
-extern int cmd_diff_stages(int argc, const char **argv, char **envp);
-extern int cmd_diff_tree(int argc, const char **argv, char **envp);
-extern int cmd_cat_file(int argc, const char **argv, char **envp);
-extern int cmd_rev_parse(int argc, const char **argv, char **envp);
-extern int cmd_update_index(int argc, const char **argv, char **envp);
-extern int cmd_update_ref(int argc, const char **argv, char **envp);
-extern int cmd_fmt_merge_msg(int argc, const char **argv, char **envp);
+extern int cmd_push(int argc, const char **argv, const char *prefix);
+extern int cmd_grep(int argc, const char **argv, const char *prefix);
+extern int cmd_rm(int argc, const char **argv, const char *prefix);
+extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
+extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
+extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_apply(int argc, const char **argv, const char *prefix);
+extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
+extern int cmd_update_index(int argc, const char **argv, const char *prefix);
+extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+extern int cmd_mv(int argc, const char **argv, const char *prefix);
 
-extern int cmd_write_tree(int argc, const char **argv, char **envp);
+extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 
-extern int cmd_mailsplit(int argc, const char **argv, char **envp);
+extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
 
-extern int cmd_mailinfo(int argc, const char **argv, char **envp);
+extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
 
-extern int cmd_stripspace(int argc, const char **argv, char **envp);
+extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern void stripspace(FILE *in, FILE *out);
 #endif
diff --git a/cache.h b/cache.h
index 8cc0ccbd0fa81eb72161b77e82f5fda201fdbdd9..7c69b30637406a17fd6ccadcfa58a2a809fba0bb 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -155,6 +155,7 @@ extern int add_cache_entry(struct cache_entry *ce, int option);
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern int remove_cache_entry_at(int pos);
 extern int remove_file_from_cache(const char *path);
+extern int add_file_to_index(const char *path, int verbose);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
 extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
@@ -180,6 +181,7 @@ extern int commit_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
 
 /* Environment bits from configuration mechanism */
+extern int use_legacy_headers;
 extern int trust_executable_bit;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
index 1bc1484645b4021ee71415f3b3ed44e4c88030d6..919112bba9271a8e3903b06946cf91036411f1a4 100644 (file)
@@ -639,8 +639,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        /* deleted file */
                        result_size = 0;
                        elem->mode = 0;
-                       result = xmalloc(1);
-                       result[0] = 0;
+                       result = xcalloc(1, 1);
                }
                if (0 <= fd)
                        close(fd);
index 8445f7dcab3bdf96326b8d6a3e1b8c8a8161cb23..0ac6aebbbcbd666d5bd8201ce28e1868bd6d5a30 100644 (file)
--- a/config.c
+++ b/config.c
@@ -279,6 +279,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.legacyheaders")) {
+               use_legacy_headers = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.compression")) {
                int level = git_config_int(var, value);
                if (level == -1)
diff --git a/config.mak.in b/config.mak.in
new file mode 100644 (file)
index 0000000..04f508a
--- /dev/null
@@ -0,0 +1,24 @@
+# git Makefile configuration, included in main Makefile
+# @configure_input@
+
+CC = @CC@
+AR = @AR@
+TAR = @TAR@
+#INSTALL = @INSTALL@           # needs install-sh or install.sh in sources
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+#gitexecdir = @libexecdir@/git-core/
+datarootdir = @datarootdir@
+template_dir = @datadir@/git-core/templates/
+GIT_PYTHON_DIR = @datadir@/git-core/python
+
+mandir=@mandir@
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+export exec_prefix mandir
+export srcdir VPATH
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..c1f7751
--- /dev/null
@@ -0,0 +1,183 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT([git], [1.4.1], [git@vger.kernel.org])
+
+AC_CONFIG_SRCDIR([git.c])
+
+config_file=config.mak.autogen
+config_append=config.mak.append
+config_in=config.mak.in
+
+echo "# ${config_append}.  Generated by configure." > "${config_append}"
+
+
+## Definitions of macros
+# GIT_CONF_APPEND_LINE(LINE)
+# --------------------------
+# Append LINE to file ${config_append}
+AC_DEFUN([GIT_CONF_APPEND_LINE],
+[echo "$1" >> "${config_append}"])# GIT_CONF_APPEND_LINE
+
+
+## Checks for programs.
+AC_MSG_NOTICE([CHECKS for programs])
+#
+AC_PROG_CC
+#AC_PROG_INSTALL               # needs install-sh or install.sh in sources
+AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_PROGS(TAR, [gtar tar])
+#
+# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
+
+
+## Checks for libraries.
+AC_MSG_NOTICE([CHECKS for libraries])
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+AC_CHECK_LIB([ssl], [SHA1_Init],[],
+[AC_CHECK_LIB([crypto], [SHA1_INIT],
+ [GIT_CONF_APPEND_LINE(NEEDS_SSL_WITH_CRYPTO=YesPlease)],
+ [GIT_CONF_APPEND_LINE(NO_OPENSSL=YesPlease)])])
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+AC_CHECK_LIB([curl], [curl_global_init],[],
+[GIT_CONF_APPEND_LINE(NO_CURL=YesPlease)])
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+AC_CHECK_LIB([expat], [XML_ParserCreate],[],
+[GIT_CONF_APPEND_LINE(NO_EXPAT=YesPlease)])
+#
+# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
+AC_CHECK_LIB([c], [iconv],[],
+[AC_CHECK_LIB([iconv],[iconv],
+ [GIT_CONF_APPEND_LINE(NEEDS_LIBICONV=YesPlease)],[])])
+#
+# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
+# Patrick Mauritz).
+AC_CHECK_LIB([c], [socket],[],
+[AC_CHECK_LIB([socket],[socket],
+ [GIT_CONF_APPEND_LINE(NEEDS_SOCKET=YesPlease)],[])])
+
+
+## Checks for header files.
+
+
+## Checks for typedefs, structures, and compiler characteristics.
+AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
+#
+# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
+AC_CHECK_MEMBER(struct dirent.d_ino,[],
+[GIT_CONF_APPEND_LINE(NO_D_INO_IN_DIRENT=YesPlease)],
+[#include <dirent.h>])
+#
+# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
+# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
+AC_CHECK_MEMBER(struct dirent.d_type,[],
+[GIT_CONF_APPEND_LINE(NO_D_TYPE_IN_DIRENT=YesPlease)],
+[#include <dirent.h>])
+#
+# Define NO_SOCKADDR_STORAGE if your platform does not have struct
+# sockaddr_storage.
+AC_CHECK_TYPE(struct sockaddr_storage,[],
+[GIT_CONF_APPEND_LINE(NO_SOCKADDR_STORAGE=YesPlease)],
+[#include <netinet/in.h>])
+
+
+## Checks for library functions.
+## (in default C library and libraries checked by AC_CHECK_LIB)
+AC_MSG_NOTICE([CHECKS for library functions])
+#
+# Define NO_STRCASESTR if you don't have strcasestr.
+AC_CHECK_FUNC(strcasestr,[],
+[GIT_CONF_APPEND_LINE(NO_STRCASESTR=YesPlease)])
+#
+# Define NO_STRLCPY if you don't have strlcpy.
+AC_CHECK_FUNC(strlcpy,[],
+[GIT_CONF_APPEND_LINE(NO_STRLCPY=YesPlease)])
+#
+# Define NO_SETENV if you don't have setenv in the C library.
+AC_CHECK_FUNC(setenv,[],
+[GIT_CONF_APPEND_LINE(NO_SETENV=YesPlease)])
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+
+
+## Other checks.
+# Define USE_PIC if you need the main git objects to be built with -fPIC
+# in order to build and link perl/Git.so.  x86-64 seems to need this.
+#
+# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
+# Enable it on Windows.  By default, symrefs are still used.
+#
+# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
+#
+# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
+# a missing newline at the end of the file.
+
+
+## Site configuration
+## --with-PACKAGE[=ARG] and --without-PACKAGE
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interopability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# 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 PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+#
+## --enable-FEATURE[=ARG] and --disable-FEATURE
+# 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.
+
+
+## Output files
+AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"])
+AC_OUTPUT
+
+
+## Cleanup
+rm -f "${config_append}"
index d3619db510f4e4878eba49fcd80da3e78d564911..350846de904b31fb9dd86fee0c64613a7632c6aa 100644 (file)
@@ -3,9 +3,9 @@
 EMACS = emacs
 
 ELC = git.elc vc-git.elc
-INSTALL = install
+INSTALL ?= install
 INSTALL_ELC = $(INSTALL) -m 644
-prefix = $(HOME)
+prefix ?= $(HOME)
 emacsdir = $(prefix)/share/emacs/site-lisp
 
 all: $(ELC)
index 34c995046d8cd783cf165e8d2e40cb0d11f85f59..68de9be0c7cca8645275a042900766b6ea2712c7 100644 (file)
@@ -55,7 +55,8 @@
 ;;;; ------------------------------------------------------------
 
 (defgroup git nil
-  "Git user interface")
+  "A user interface for the git versioning system."
+  :group 'tools)
 
 (defcustom git-committer-name nil
   "User name to use for commits.
@@ -83,6 +84,12 @@ then to `add-log-mailing-address' and then to `user-mail-address'."
   :group 'git
   :type 'boolean)
 
+(defcustom git-reuse-status-buffer t
+  "Whether `git-status' should try to reuse an existing buffer
+if there is already one that displays the same directory."
+  :group 'git
+  :type 'boolean)
+
 (defcustom git-per-dir-ignore-file ".gitignore"
   "Name of the per-directory ignore file."
   :group 'git
@@ -258,7 +265,7 @@ and returns the process output as a string."
     (set-buffer (find-file-noselect ignore-name))
     (goto-char (point-max))
     (unless (zerop (current-column)) (insert "\n"))
-    (insert name "\n")
+    (insert "/" name "\n")
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
@@ -584,6 +591,8 @@ and returns the process output as a string."
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
                             (with-current-buffer buffer (erase-buffer))
                             (git-set-files-state files 'uptodate)
+                            (when (file-directory-p ".git/rr-cache")
+                              (git-run-command nil nil "rerere"))
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
                             (message "Committed %s." commit))
@@ -1001,12 +1010,28 @@ Commands:
   (set (make-local-variable 'list-buffers-directory) default-directory)
   (run-hooks 'git-status-mode-hook)))
 
+(defun git-find-status-buffer (dir)
+  "Find the git status buffer handling a specified directory."
+  (let ((list (buffer-list))
+        (fulldir (expand-file-name dir))
+        found)
+    (while (and list (not found))
+      (let ((buffer (car list)))
+        (with-current-buffer buffer
+          (when (and list-buffers-directory
+                     (string-equal fulldir (expand-file-name list-buffers-directory))
+                     (string-match "\\*git-status\\*$" (buffer-name buffer)))
+            (setq found buffer))))
+      (setq list (cdr list)))
+    found))
+
 (defun git-status (dir)
   "Entry point into git-status mode."
   (interactive "DSelect directory: ")
   (setq dir (git-get-top-dir dir))
   (if (file-directory-p (concat (file-name-as-directory dir) ".git"))
-      (let ((buffer (create-file-buffer (expand-file-name "*git-status*" dir))))
+      (let ((buffer (or (and git-reuse-status-buffer (git-find-status-buffer dir))
+                        (create-file-buffer (expand-file-name "*git-status*" dir)))))
         (switch-to-buffer buffer)
         (cd dir)
         (git-status-mode)
index e4ec676301c965e0b0ec81995f4e1fb77363c373..810837f0c4b8296ce762441c80527efdb6c9c3b6 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -19,7 +19,7 @@ static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
 "           [--base-path=path] [--user-path | --user-path=path]\n"
-"           [--reuseaddr] [directory...]";
+"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
diff --git a/diff.c b/diff.c
index 8b44756136dbb040c7c82e07ca535be0b66ffb30..6a713764831e7521958f4b280f2e1d3911fb3cea 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -17,15 +17,6 @@ static int diff_detect_rename_default = 0;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default = 0;
 
-enum color_diff {
-       DIFF_RESET = 0,
-       DIFF_PLAIN = 1,
-       DIFF_METAINFO = 2,
-       DIFF_FRAGINFO = 3,
-       DIFF_FILE_OLD = 4,
-       DIFF_FILE_NEW = 5,
-};
-
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 static char diff_colors[][24] = {
        "\033[m",       /* reset */
@@ -33,7 +24,8 @@ static char diff_colors[][24] = {
        "\033[1m",      /* bold */
        "\033[36m",     /* cyan */
        "\033[31m",     /* red */
-       "\033[32m"      /* green */
+       "\033[32m",     /* green */
+       "\033[33m"      /* yellow */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -48,6 +40,8 @@ static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_FILE_OLD;
        if (!strcasecmp(var+ofs, "new"))
                return DIFF_FILE_NEW;
+       if (!strcasecmp(var+ofs, "commit"))
+               return DIFF_COMMIT;
        die("bad config variable '%s'", var);
 }
 
@@ -370,7 +364,7 @@ struct emit_callback {
        const char **label_path;
 };
 
-static inline const char *get_color(int diff_use_color, enum color_diff ix)
+const char *diff_get_color(int diff_use_color, enum color_diff ix)
 {
        if (diff_use_color)
                return diff_colors[ix];
@@ -381,8 +375,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        int i;
        struct emit_callback *ecbdata = priv;
-       const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 
        if (ecbdata->label_path[0]) {
                printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
@@ -397,7 +391,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ;
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
-               set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+               set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        }
        else if (len < ecbdata->nparents)
                set = reset;
@@ -410,7 +404,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        else if (line[i] == '+')
                                color = DIFF_FILE_NEW;
                }
-               set = get_color(ecbdata->color_diff, color);
+               set = diff_get_color(ecbdata->color_diff, color);
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
@@ -767,8 +761,8 @@ static void builtin_diff(const char *name_a,
        mmfile_t mf1, mf2;
        const char *lbl[2];
        char *a_one, *b_two;
-       const char *set = get_color(o->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(o->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
 
        a_one = quote_two("a/", name_a);
        b_two = quote_two("b/", name_b);
diff --git a/diff.h b/diff.h
index a06f959938d1313e45daa610a1f3eb71b4c3f835..2cced530fa60ad7206b2cdb1b2509f42469e8f73 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -69,6 +69,17 @@ struct diff_options {
        add_remove_fn_t add_remove;
 };
 
+enum color_diff {
+       DIFF_RESET = 0,
+       DIFF_PLAIN = 1,
+       DIFF_METAINFO = 2,
+       DIFF_FRAGINFO = 3,
+       DIFF_FILE_OLD = 4,
+       DIFF_FILE_NEW = 5,
+       DIFF_COMMIT = 6,
+};
+const char *diff_get_color(int diff_use_color, enum color_diff ix);
+
 extern const char mime_boundary_leader[];
 
 extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
index 97d42b172be5abbada30da97893c77e67a0bcde0..42f39d657ef0056667bb8814622e0dde2733e373 100644 (file)
@@ -11,6 +11,7 @@
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
+int use_legacy_headers = 1;
 int trust_executable_bit = 1;
 int assume_unchanged = 0;
 int prefer_symlink_refs = 0;
diff --git a/fetch.c b/fetch.c
index 989d7a47884ec7bac05d329baeb97bdb12b7b22e..aeb6bf2639cc75b6f738348bcc267179a8a0da65 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -7,9 +7,7 @@
 #include "tag.h"
 #include "blob.h"
 #include "refs.h"
-
-const char *write_ref = NULL;
-const char *write_ref_log_details = NULL;
+#include "strbuf.h"
 
 int get_tree = 0;
 int get_history = 0;
@@ -213,54 +211,106 @@ static int mark_complete(const char *path, const unsigned char *sha1)
        return 0;
 }
 
-int pull(char *target)
+int pull_targets_stdin(char ***target, const char ***write_ref)
+{
+       int targets = 0, targets_alloc = 0;
+       struct strbuf buf;
+       *target = NULL; *write_ref = NULL;
+       strbuf_init(&buf);
+       while (1) {
+               char *rf_one = NULL;
+               char *tg_one;
+
+               read_line(&buf, stdin, '\n');
+               if (buf.eof)
+                       break;
+               tg_one = buf.buf;
+               rf_one = strchr(tg_one, '\t');
+               if (rf_one)
+                       *rf_one++ = 0;
+
+               if (targets >= targets_alloc) {
+                       targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
+                       *target = xrealloc(*target, targets_alloc * sizeof(**target));
+                       *write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
+               }
+               (*target)[targets] = strdup(tg_one);
+               (*write_ref)[targets] = rf_one ? strdup(rf_one) : NULL;
+               targets++;
+       }
+       return targets;
+}
+
+void pull_targets_free(int targets, char **target, const char **write_ref)
 {
-       struct ref_lock *lock = NULL;
-       unsigned char sha1[20];
+       while (targets--) {
+               free(target[targets]);
+               if (write_ref && write_ref[targets])
+                       free((char *) write_ref[targets]);
+       }
+}
+
+int pull(int targets, char **target, const char **write_ref,
+         const char *write_ref_log_details)
+{
+       struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
+       unsigned char *sha1 = xmalloc(targets * 20);
        char *msg;
        int ret;
+       int i;
 
        save_commit_buffer = 0;
        track_object_refs = 0;
-       if (write_ref) {
-               lock = lock_ref_sha1(write_ref, NULL, 0);
-               if (!lock) {
-                       error("Can't lock ref %s", write_ref);
-                       return -1;
+
+       for (i = 0; i < targets; i++) {
+               if (!write_ref || !write_ref[i])
+                       continue;
+
+               lock[i] = lock_ref_sha1(write_ref[i], NULL, 0);
+               if (!lock[i]) {
+                       error("Can't lock ref %s", write_ref[i]);
+                       goto unlock_and_fail;
                }
        }
 
        if (!get_recover)
                for_each_ref(mark_complete);
 
-       if (interpret_target(target, sha1)) {
-               error("Could not interpret %s as something to pull", target);
-               if (lock)
-                       unlock_ref(lock);
-               return -1;
+       for (i = 0; i < targets; i++) {
+               if (interpret_target(target[i], &sha1[20 * i])) {
+                       error("Could not interpret %s as something to pull", target[i]);
+                       goto unlock_and_fail;
+               }
+               if (process(lookup_unknown_object(&sha1[20 * i])))
+                       goto unlock_and_fail;
        }
-       if (process(lookup_unknown_object(sha1))) {
-               if (lock)
-                       unlock_ref(lock);
-               return -1;
+
+       if (loop())
+               goto unlock_and_fail;
+
+       if (write_ref_log_details) {
+               msg = xmalloc(strlen(write_ref_log_details) + 12);
+               sprintf(msg, "fetch from %s", write_ref_log_details);
+       } else {
+               msg = NULL;
        }
-       if (loop()) {
-               if (lock)
-                       unlock_ref(lock);
-               return -1;
+       for (i = 0; i < targets; i++) {
+               if (!write_ref || !write_ref[i])
+                       continue;
+               ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+               lock[i] = NULL;
+               if (ret)
+                       goto unlock_and_fail;
        }
+       if (msg)
+               free(msg);
 
-       if (write_ref) {
-               if (write_ref_log_details) {
-                       msg = xmalloc(strlen(write_ref_log_details) + 12);
-                       sprintf(msg, "fetch from %s", write_ref_log_details);
-               }
-               else
-                       msg = NULL;
-               ret = write_ref_sha1(lock, sha1, msg ? msg : "fetch (unknown)");
-               if (msg)
-                       free(msg);
-               return ret;
-       }
        return 0;
+
+
+unlock_and_fail:
+       for (i = 0; i < targets; i++)
+               if (lock[i])
+                       unlock_ref(lock[i]);
+       return -1;
 }
diff --git a/fetch.h b/fetch.h
index 841bb1af9cab0c19dda3f0cb5956edaec22f9d3a..be48c6f19092a81672dd24eb38e9ffca39f5b53c 100644 (file)
--- a/fetch.h
+++ b/fetch.h
@@ -22,12 +22,6 @@ extern void prefetch(unsigned char *sha1);
  */
 extern int fetch_ref(char *ref, unsigned char *sha1);
 
-/* If set, the ref filename to write the target value to. */
-extern const char *write_ref;
-
-/* If set additional text will appear in the ref log. */
-extern const char *write_ref_log_details;
-
 /* Set to fetch the target tree. */
 extern int get_tree;
 
@@ -46,6 +40,15 @@ extern int get_recover;
 /* Report what we got under get_verbosely */
 extern void pull_say(const char *, const char *);
 
-extern int pull(char *target);
+/* Load pull targets from stdin */
+extern int pull_targets_stdin(char ***target, const char ***write_ref);
+
+/* Free up loaded targets */
+extern void pull_targets_free(int targets, char **target, const char **write_ref);
+
+/* If write_ref is set, the ref filename to write the target value to. */
+/* If write_ref_log_details is set, additional text will appear in the ref log. */
+extern int pull(int targets, char **target, const char **write_ref,
+               const char *write_ref_log_details);
 
 #endif /* PULL_H */
index db9a20a811199666fb64f34f1e54e0c841f494ee..04f01194356ea0c91a01eed682b068c15e313ee6 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -91,6 +91,7 @@ fall_back_3way () {
 }
 
 prec=4
+rloga=am
 dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
 
 while case "$#" in 0) break;; esac
@@ -130,6 +131,9 @@ do
        --resolvemsg=*)
        resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
 
+       --reflog-action=*)
+       rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
+
        --)
        shift; break ;;
        -*)
@@ -152,8 +156,10 @@ fi
 
 if test -d "$dotest"
 then
-       test ",$#," = ",0," ||
-       die "previous dotest directory $dotest still exists but mbox given."
+       if test ",$#," != ",0," || ! tty -s
+       then
+               die "previous dotest directory $dotest still exists but mbox given."
+       fi
        resume=yes
 else
        # Make sure we are not given --skip nor --resolved
@@ -413,7 +419,7 @@ do
        parent=$(git-rev-parse --verify HEAD) &&
        commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
        echo Committed: $commit &&
-       git-update-ref -m "am: $SUBJECT" HEAD $commit $parent ||
+       git-update-ref -m "$rloga: $SUBJECT" HEAD $commit $parent ||
        stop_here $this
 
        if test -x "$GIT_DIR"/hooks/post-applypatch
index 036880388370a955deba19da1194600a420baef4..a92b22a13d6b2932a8d816a8845d791c2f0e36d1 100755 (executable)
@@ -266,7 +266,7 @@ yes,yes)
            echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
            ;;
        esac
-       git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
+       git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
        ;;
 *)
        case "$repo" in
@@ -296,7 +296,7 @@ yes,yes)
                    done
                    rm -f "$GIT_DIR/TMP_ALT"
                fi
-               git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
+               git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
                ;;
        http://*)
                if test -z "@@NO_CURL@@"
index 5d13a54194d23ceaa50bc52a4f192156898d661a..99b3dc392afdd5ffb3e2aa8f6fe6b484c2e2f1fe 100755 (executable)
@@ -16,9 +16,9 @@
     die "GIT_DIR is not defined or is unreadable";
 }
 
-our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_m );
+our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m );
 
-getopts('hpvcfm:');
+getopts('hpvcfam:');
 
 $opt_h && usage();
 
@@ -29,7 +29,6 @@
                                     TMPDIR => 1,
                                     CLEANUP => 1);
 
-print Dumper(@ARGV);
 # resolve target commit
 my $commit;
 $commit = pop @ARGV;
 # find parents from the commit itself
 my @commit  = safe_pipe_capture('git-cat-file', 'commit', $commit);
 my @parents;
-foreach my $p (@commit) {
-    if ($p =~ m/^$/) { # end of commit headers, we're done
-       last;
+my $committer;
+my $author;
+my $stage = 'headers'; # headers, msg
+my $title;
+my $msg = '';
+
+foreach my $line (@commit) {
+    chomp $line;
+    if ($stage eq 'headers' && $line eq '') {
+       $stage = 'msg';
+       next;
     }
-    if ($p =~ m/^parent (\w{40})$/) { # found a parent
-       push @parents, $1;
+
+    if ($stage eq 'headers') {
+       if ($line =~ m/^parent (\w{40})$/) { # found a parent
+           push @parents, $1;
+       } elsif ($line =~ m/^author (.+) \d+ \+\d+$/) {
+           $author = $1;
+       } elsif ($line =~ m/^committer (.+) \d+ \+\d+$/) {
+           $committer = $1;
+       }
+    } else {
+       $msg .= $line . "\n";
+       unless ($title) {
+           $title = $line;
+       }
     }
 }
 
 
 # grab the commit message
 open(MSG, ">.msg") or die "Cannot open .msg for writing";
-print MSG $opt_m;
+if ($opt_m) {
+    print MSG $opt_m;
+}
+print MSG $msg;
+if ($opt_a) {
+    print MSG "\n\nAuthor: $author\n";
+    if ($author ne $committer) {
+       print MSG "Committer: $committer\n";
+    }
+}
 close MSG;
 
-`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
-$? && die "Error extracting the commit message";
-
 my (@afiles, @dfiles, @mfiles, @dirs);
 my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
 #print @files;
 }
 
 print "Commit to CVS\n";
+print "Patch: $title\n";
 my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
 my $cmd = "cvs commit -F .msg $commitfiles";
 
index c30ef7042751034c5efbc4c2752e5badc69d4f4e..2130d57020de2dae570b7be0d9a516389bd1829f 100755 (executable)
@@ -81,7 +81,7 @@
 
 # $state holds all the bits of information the clients sends us that could
 # potentially be useful when it comes to actually _doing_ something.
-my $state = {};
+my $state = { prependdir => '' };
 $log->info("--------------- STARTING -----------------");
 
 my $TEMP_DIR = tempdir( CLEANUP => 1 );
@@ -547,12 +547,15 @@ sub req_Argument
 {
     my ( $cmd, $data ) = @_;
 
-    # TODO :  Not quite sure how Argument and Argumentx differ, but I assume
-    # it's for multi-line arguments ... somehow ...
+    # Argumentx means: append to last Argument (with a newline in front)
 
     $log->debug("$cmd : $data");
 
-    push @{$state->{arguments}}, $data;
+    if ( $cmd eq 'Argumentx') {
+        ${$state->{arguments}}[$#{$state->{arguments}}] .= "\n" . $data;
+    } else {
+        push @{$state->{arguments}}, $data;
+    }
 }
 
 # expand-modules \n
@@ -1139,9 +1142,7 @@ sub req_ci
         exit;
     }
 
-    open FILE, ">", "$ENV{GIT_DIR}refs/heads/$state->{module}";
-    print FILE $commithash;
-    close FILE;
+    print LOCKFILE $commithash;
 
     $updater->update();
 
@@ -1168,7 +1169,9 @@ sub req_ci
     }
 
     close LOCKFILE;
-    unlink($lockfile);
+    my $reffile = "$ENV{GIT_DIR}refs/heads/$state->{module}";
+    unlink($reffile);
+    rename($lockfile, $reffile);
     chdir "/";
 
     print "ok\n";
@@ -2129,12 +2132,6 @@ sub update
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    # prepare database queries
-    my $db_insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-    my $db_insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
-    my $db_delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
-    my $db_insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-
     my $commitinfo = `git-cat-file commit $self->{module} 2>&1`;
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
@@ -2323,7 +2320,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "M" )
                 {
@@ -2337,7 +2334,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "A" )
                 {
@@ -2351,7 +2348,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 else
                 {
@@ -2408,7 +2405,7 @@ sub update
                     };
 
 
-                    $db_insert_rev->execute($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
             }
             close FILELIST;
@@ -2424,7 +2421,7 @@ sub update
                     $head->{$file}{modified} = $commit->{date};
                     $head->{$file}{author} = $commit->{author};
 
-                    $db_insert_rev->execute($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $commit->{date}, $commit->{author}, $head->{$file}{mode});
+                    $self->insert_rev($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $commit->{date}, $commit->{author}, $head->{$file}{mode});
                 }
             }
             # END : "Detect deleted files"
@@ -2433,7 +2430,7 @@ sub update
 
         if (exists $commit->{mergemsg})
         {
-            $db_insert_mergelog->execute($commit->{hash}, $commit->{mergemsg});
+            $self->insert_mergelog($commit->{hash}, $commit->{mergemsg});
         }
 
         $lastpicked = $commit->{hash};
@@ -2441,10 +2438,10 @@ sub update
         $self->_set_prop("last_commit", $commit->{hash});
     }
 
-    $db_delete_head->execute();
+    $self->delete_head();
     foreach my $file ( keys %$head )
     {
-        $db_insert_head->execute(
+        $self->insert_head(
             $file,
             $head->{$file}{revision},
             $head->{$file}{filehash},
@@ -2462,6 +2459,54 @@ sub update
     $self->{dbh}->commit() or die "Failed to commit changes to SQLite";
 }
 
+sub insert_rev
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
+sub insert_mergelog
+{
+    my $self = shift;
+    my $key = shift;
+    my $value = shift;
+
+    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
+    $insert_mergelog->execute($key, $value);
+}
+
+sub delete_head
+{
+    my $self = shift;
+
+    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
+    $delete_head->execute();
+}
+
+sub insert_head
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
 sub _headrev
 {
     my $self = shift;
index ff1769952b704d3ee3123395274da76e8f09a373..c2eebee798d10f7ed5977fc4e56cf02731031971 100755 (executable)
@@ -20,6 +20,7 @@ verbose=
 update_head_ok=
 exec=
 upload_pack=
+keep=--thin
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -69,7 +70,8 @@ case "$#" in
 0)
        test -f "$GIT_DIR/branches/origin" ||
                test -f "$GIT_DIR/remotes/origin" ||
-                       die "Where do you want to fetch from today?"
+                       git-repo-config --get remote.origin.url >/dev/null ||
+                               die "Where do you want to fetch from today?"
        set origin ;;
 esac
 
@@ -153,7 +155,7 @@ fast_forward_local () {
        then
                if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
                then
-                       [ "$verbose" ] && echo >&2 "* $1: same as $3"
+                       [ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
                else
                        echo >&2 "* $1: updating with $3"
                        git-update-ref -m "$rloga: updating tag" "$1" "$2"
@@ -223,9 +225,16 @@ reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
        taglist=`IFS="  " &&
-                 git-ls-remote $upload_pack --tags "$remote" |
+                 (
+                       git-ls-remote $upload_pack --tags "$remote" ||
+                       echo fail ouch
+                 ) |
                  while read sha1 name
                  do
+                       case "$sha1" in
+                       fail)
+                               exit 1
+                       esac
                        case "$name" in
                        *^*) continue ;;
                        esac
@@ -235,7 +244,7 @@ then
                        else
                            echo >&2 "warning: tag ${name} ignored"
                        fi
-                 done`
+                 done` || exit
        if test "$#" -gt 1
        then
                # remote URL plus explicit refspecs; we need to merge them.
@@ -347,7 +356,7 @@ fetch_main () {
     ( : subshell because we muck with IFS
       IFS="    $LF"
       (
-         git-fetch-pack $exec $keep --thin "$remote" $rref || echo failed "$remote"
+         git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
       ) |
       while read sha1 remote_name
       do
index 63b18b99f65fe7ac60a76a0dfd2662d7400696c8..16cd351f7f0d992be37be83017961c70731d4cc5 100755 (executable)
@@ -54,6 +54,10 @@ start_httpd () {
                        fi
                done
        fi
+       if test $? != 0; then
+               echo "Could not execute http daemon $httpd."
+               exit 1
+       fi
 }
 
 stop_httpd () {
@@ -183,8 +187,10 @@ PerlPassEnv GIT_EXEC_DIR
 EOF
        else
                # plain-old CGI
+               list_mods=`echo "$httpd" | sed "s/-f$/-l/"`
+               $list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
+               echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
                cat >> "$conf" <<EOF
-LoadModule cgi_module $module_path/mod_cgi.so
 AddHandler cgi-script .cgi
 <Location /gitweb.cgi>
        Options +ExecCGI
@@ -232,4 +238,5 @@ esac
 
 start_httpd
 test -z "$browser" && browser=echo
-$browser http://127.0.0.1:$port
+url=http://127.0.0.1:$port
+$browser $url || echo $url
index ba6d587f3199e5847dc942810ea85b2dbec656b9..b928f2ca52c1991ee50838fc47580ed83c58fb64 100755 (executable)
@@ -12,7 +12,7 @@ fi
 laf="$GIT_DIR/lost-found"
 rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
 
-git fsck-objects |
+git fsck-objects --full |
 while read dangling type sha1
 do
        case "$dangling" in
index 9b681159dcd5e1e22886d101306e496528cb60a4..d049e164318a416941334b15699d52f87b00387f 100755 (executable)
@@ -63,7 +63,13 @@ squash_message () {
 }
 
 finish () {
-       test '' = "$2" || echo "$2"
+       if test '' = "$2"
+       then
+               rlogm="$rloga"
+       else
+               echo "$2"
+               rlogm="$rloga: $2"
+       fi
        case "$squash" in
        t)
                echo "Squash commit -- not updating HEAD"
@@ -75,7 +81,7 @@ finish () {
                        echo "No merge message -- not updating HEAD"
                        ;;
                *)
-                       git-update-ref HEAD "$1" "$head" || exit 1
+                       git-update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
                        ;;
                esac
                ;;
@@ -93,6 +99,7 @@ finish () {
        esac
 }
 
+rloga=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -126,6 +133,9 @@ do
                        die "available strategies are: $all_strategies" ;;
                esac
                ;;
+       --reflog-action=*)
+               rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               ;;
        -*)     usage ;;
        *)      break ;;
        esac
@@ -140,6 +150,7 @@ shift
 
 # All the rest are remote heads
 test "$#" = 0 && usage ;# we need at least one remote head.
+test "$rloga" = '' && rloga="merge: $@"
 
 remoteheads=
 for remote
@@ -325,7 +336,7 @@ if test '' != "$result_tree"
 then
     parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
     result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree $parents) || exit
-    finish "$result_commit" "Merge $result_commit, made by $wt_strategy."
+    finish "$result_commit" "Merge made by $wt_strategy."
     dropsave
     exit 0
 fi
diff --git a/git-mv.perl b/git-mv.perl
deleted file mode 100755 (executable)
index 75aa8fe..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/usr/bin/perl
-#
-# Copyright 2005, Ryan Anderson <ryan@michonline.com>
-#                 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
-#
-# This file is licensed under the GPL v2, or a later version
-# at the discretion of Linus Torvalds.
-
-
-use warnings;
-use strict;
-use Getopt::Std;
-
-sub usage() {
-       print <<EOT;
-$0 [-f] [-n] <source> <destination>
-$0 [-f] [-n] [-k] <source> ... <destination directory>
-EOT
-       exit(1);
-}
-
-our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
-getopts("hnfkv") || usage;
-usage() if $opt_h;
-@ARGV >= 1 or usage;
-
-my $GIT_DIR = `git rev-parse --git-dir`;
-exit 1 if $?; # rev-parse would have given "not a git dir" message.
-chomp($GIT_DIR);
-
-my (@srcArgs, @dstArgs, @srcs, @dsts);
-my ($src, $dst, $base, $dstDir);
-
-# remove any trailing slash in arguments
-for (@ARGV) { s/\/*$//; }
-
-my $argCount = scalar @ARGV;
-if (-d $ARGV[$argCount-1]) {
-       $dstDir = $ARGV[$argCount-1];
-       @srcArgs = @ARGV[0..$argCount-2];
-
-       foreach $src (@srcArgs) {
-               $base = $src;
-               $base =~ s/^.*\///;
-               $dst = "$dstDir/". $base;
-               push @dstArgs, $dst;
-       }
-}
-else {
-    if ($argCount < 2) {
-       print "Error: need at least two arguments\n";
-       exit(1);
-    }
-    if ($argCount > 2) {
-       print "Error: moving to directory '"
-           . $ARGV[$argCount-1]
-           . "' not possible; not existing\n";
-       exit(1);
-    }
-    @srcArgs = ($ARGV[0]);
-    @dstArgs = ($ARGV[1]);
-    $dstDir = "";
-}
-
-my $subdir_prefix = `git rev-parse --show-prefix`;
-chomp($subdir_prefix);
-
-# run in git base directory, so that git-ls-files lists all revisioned files
-chdir "$GIT_DIR/..";
-
-# normalize paths, needed to compare against versioned files and update-index
-# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
-for (@srcArgs, @dstArgs) {
-    # prepend git prefix as we run from base directory
-    $_ = $subdir_prefix.$_;
-    s|^\./||;
-    s|/\./|/| while (m|/\./|);
-    s|//+|/|g;
-    # Also "a/b/../c" ==> "a/c"
-    1 while (s,(^|/)[^/]+/\.\./,$1,);
-}
-
-my (@allfiles,@srcfiles,@dstfiles);
-my $safesrc;
-my (%overwritten, %srcForDst);
-
-$/ = "\0";
-open(F, 'git-ls-files -z |')
-        or die "Failed to open pipe from git-ls-files: " . $!;
-
-@allfiles = map { chomp; $_; } <F>;
-close(F);
-
-
-my ($i, $bad);
-while(scalar @srcArgs > 0) {
-    $src = shift @srcArgs;
-    $dst = shift @dstArgs;
-    $bad = "";
-
-    for ($src, $dst) {
-       # Be nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
-       s|^\./||;
-       s|/\./|/| while (m|/\./|);
-       s|//+|/|g;
-       # Also "a/b/../c" ==> "a/c"
-       1 while (s,(^|/)[^/]+/\.\./,$1,);
-    }
-
-    if ($opt_v) {
-       print "Checking rename of '$src' to '$dst'\n";
-    }
-
-    unless (-f $src || -l $src || -d $src) {
-       $bad = "bad source '$src'";
-    }
-
-    $safesrc = quotemeta($src);
-    @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
-
-    $overwritten{$dst} = 0;
-    if (($bad eq "") && -e $dst) {
-       $bad = "destination '$dst' already exists";
-       if ($opt_f) {
-           # only files can overwrite each other: check both source and destination
-           if (-f $dst && (scalar @srcfiles == 1)) {
-               print "Warning: $bad; will overwrite!\n";
-               $bad = "";
-               $overwritten{$dst} = 1;
-           }
-           else {
-               $bad = "Can not overwrite '$src' with '$dst'";
-           }
-       }
-    }
-    
-    if (($bad eq "") && ($dst =~ /^$safesrc\//)) {
-       $bad = "can not move directory '$src' into itself";
-    }
-
-    if ($bad eq "") {
-        if (scalar @srcfiles == 0) {
-           $bad = "'$src' not under version control";
-       }
-    }
-
-    if ($bad eq "") {
-       if (defined $srcForDst{$dst}) {
-           $bad = "can not move '$src' to '$dst'; already target of ";
-           $bad .= "'".$srcForDst{$dst}."'";
-       }
-       else {
-           $srcForDst{$dst} = $src;
-       }
-    }
-
-    if ($bad ne "") {
-       if ($opt_k) {
-           print "Warning: $bad; skipping\n";
-           next;
-       }
-       print "Error: $bad\n";
-       exit(1);
-    }
-    push @srcs, $src;
-    push @dsts, $dst;
-}
-
-# Final pass: rename/move
-my (@deletedfiles,@addedfiles,@changedfiles);
-$bad = "";
-while(scalar @srcs > 0) {
-    $src = shift @srcs;
-    $dst = shift @dsts;
-
-    if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; }
-    if (!$opt_n) {
-       if (!rename($src,$dst)) {
-           $bad = "renaming '$src' failed: $!";
-           if ($opt_k) {
-               print "Warning: skipped: $bad\n";
-               $bad = "";
-               next;
-           }
-           last;
-       }
-    }
-
-    $safesrc = quotemeta($src);
-    @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
-    @dstfiles = @srcfiles;
-    s/^$safesrc(\/|$)/$dst$1/ for @dstfiles;
-
-    push @deletedfiles, @srcfiles;
-    if (scalar @srcfiles == 1) {
-       # $dst can be a directory with 1 file inside
-       if ($overwritten{$dst} ==1) {
-           push @changedfiles, $dstfiles[0];
-
-       } else {
-           push @addedfiles, $dstfiles[0];
-       }
-    }
-    else {
-       push @addedfiles, @dstfiles;
-    }
-}
-
-if ($opt_n) {
-    if (@changedfiles) {
-       print "Changed  : ". join(", ", @changedfiles) ."\n";
-    }
-    if (@addedfiles) {
-       print "Adding   : ". join(", ", @addedfiles) ."\n";
-    }
-    if (@deletedfiles) {
-       print "Deleting : ". join(", ", @deletedfiles) ."\n";
-    }
-}
-else {
-    if (@changedfiles) {
-       open(H, "| git-update-index -z --stdin")
-               or die "git-update-index failed to update changed files with code $!\n";
-       foreach my $fileName (@changedfiles) {
-               print H "$fileName\0";
-       }
-       close(H);
-    }
-    if (@addedfiles) {
-       open(H, "| git-update-index --add -z --stdin")
-               or die "git-update-index failed to add new names with code $!\n";
-       foreach my $fileName (@addedfiles) {
-               print H "$fileName\0";
-       }
-       close(H);
-    }
-    if (@deletedfiles) {
-       open(H, "| git-update-index --remove -z --stdin")
-               or die "git-update-index failed to remove old names with code $!\n";
-       foreach my $fileName (@deletedfiles) {
-               print H "$fileName\0";
-       }
-       close(H);
-    }
-}
-
-if ($bad ne "") {
-    print "Error: $bad\n";
-    exit(1);
-}
index d337bf4da31c8a86eeaee7b34b910611100ccca8..f380437997f053d15a177a941f3b8a1543c56a18 100755 (executable)
@@ -102,5 +102,6 @@ case "$strategy_args" in
 esac
 
 merge_name=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
-git-merge $no_summary $no_commit $squash $strategy_args \
+git-merge "--reflog-action=pull $*" \
+       $no_summary $no_commit $squash $strategy_args \
        "$merge_name" HEAD $merge_head
index 364baff80645efb225e433dbf5845e678df60760..10135da3ac04e3175fa45ca26b5ea31605a47284 100755 (executable)
@@ -112,7 +112,7 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
                git-apply --index -C1 "$tmp_patch" &&
                tree=$(git-write-tree) &&
                commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
-               git-update-ref HEAD $commit || exit 4
+               git-update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
        fi
 done
 rm -rf $tmp_dir || exit 5
index 8c5da7219e760cd7e57b523eb626629727b1d44b..cb1a9ce12c932f532003ee78c965ee7f839bd207 100755 (executable)
@@ -137,7 +137,8 @@ do
                        finish_rb_merge
                        exit
                fi
-               git am --resolved --3way --resolvemsg="$RESOLVEMSG"
+               git am --resolved --3way --resolvemsg="$RESOLVEMSG" \
+                       --reflog-action=rebase
                exit
                ;;
        --skip)
@@ -156,7 +157,8 @@ do
                        finish_rb_merge
                        exit
                fi
-               git am -3 --skip --resolvemsg="$RESOLVEMSG"
+               git am -3 --skip --resolvemsg="$RESOLVEMSG" \
+                       --reflog-action=rebase
                exit
                ;;
        --abort)
@@ -299,7 +301,8 @@ fi
 if test -z "$do_merge"
 then
        git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
-       git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+       git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
+               --reflog-action=rebase
        exit $?
 fi
 
index 5c0224090a252bed3258bbbb127e84e61a874197..36fc8ce25b39682ba256a8d39bf9a393907ef43d 100755 (executable)
@@ -52,7 +52,8 @@ then
 else
        rm -f "$GIT_DIR/ORIG_HEAD"
 fi
-git-update-ref -m "reset $reset_type $@" HEAD "$rev"
+git-update-ref -m "reset $reset_type $*" HEAD "$rev"
+update_ref_status=$?
 
 case "$reset_type" in
 --hard )
@@ -66,3 +67,5 @@ case "$reset_type" in
 esac
 
 rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+
+exit $update_ref_status
index 1c7aaefa2528b73fea3eee22b086b94fa91854c5..a7bc680d90cb503c50d25b15bcaba662c5f5b49e 100755 (executable)
@@ -15,6 +15,7 @@ dropheads() {
 
 head=$(git-rev-parse --verify "$1"^0) &&
 merge=$(git-rev-parse --verify "$2"^0) &&
+merge_name="$2" &&
 merge_msg="$3" || usage
 
 #
@@ -43,7 +44,8 @@ case "$common" in
 "$head")
        echo "Updating from $head to $merge"
        git-read-tree -u -m $head $merge || exit 1
-       git-update-ref HEAD "$merge" "$head"
+       git-update-ref -m "resolve $merge_name: Fast forward" \
+               HEAD "$merge" "$head"
        git-diff-tree -p $head $merge | git-apply --stat
        dropheads
        exit 0
@@ -100,6 +102,7 @@ if [ $? -ne 0 ]; then
 fi
 result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge)
 echo "Committed merge $result_commit"
-git-update-ref HEAD "$result_commit" "$head"
+git-update-ref -m "resolve $merge_name: In-index merge" \
+       HEAD "$result_commit" "$head"
 git-diff-tree -p $head $result_commit | git-apply --stat
 dropheads
index 4530ffe42c66713f1dc9e79b3ad62124338405de..6453771f9c2ce27dcecb7bf208cd60feb4881aa3 100755 (executable)
 load_authors() if $_authors;
 load_all_refs() if $_branch_all_refs;
 svn_compat_check() unless $_use_lib;
-migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/;
+migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init|commit-diff)$/;
 $cmd{$cmd}->[0]->(@ARGV);
 exit 0;
 
@@ -2709,6 +2709,12 @@ sub libsvn_fetch {
                        } else {
                                die "Unrecognized action: $m, ($f r$rev)\n";
                        }
+               } elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
+                       my @traversed = ();
+                       libsvn_traverse($gui, '', $f, $rev, \@traversed);
+                       foreach (@traversed) {
+                               push @amr, [ $m, $_ ]
+                       }
                }
                $pool->clear;
        }
@@ -2778,7 +2784,7 @@ sub libsvn_parse_revision {
 }
 
 sub libsvn_traverse {
-       my ($gui, $pfx, $path, $rev) = @_;
+       my ($gui, $pfx, $path, $rev, $files) = @_;
        my $cwd = "$pfx/$path";
        my $pool = SVN::Pool->new;
        $cwd =~ s#^/+##g;
@@ -2786,10 +2792,15 @@ sub libsvn_traverse {
        foreach my $d (keys %$dirent) {
                my $t = $dirent->{$d}->kind;
                if ($t == $SVN::Node::dir) {
-                       libsvn_traverse($gui, $cwd, $d, $rev);
+                       libsvn_traverse($gui, $cwd, $d, $rev, $files);
                } elsif ($t == $SVN::Node::file) {
-                       print "\tA\t$cwd/$d\n" unless $_q;
-                       libsvn_get_file($gui, "$cwd/$d", $rev);
+                       my $file = "$cwd/$d";
+                       if (defined $files) {
+                               push @$files, $file;
+                       } else {
+                               print "\tA\t$file\n" unless $_q;
+                               libsvn_get_file($gui, $file, $rev);
+                       }
                }
        }
        $pool->clear;
@@ -2913,9 +2924,7 @@ sub libsvn_new_tree {
        }
        my ($paths, $rev, $author, $date, $msg) = @_;
        open my $gui, '| git-update-index -z --index-info' or croak $!;
-       my $pool = SVN::Pool->new;
-       libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool);
-       $pool->clear;
+       libsvn_traverse($gui, '', $SVN_PATH, $rev);
        close $gui or croak $?;
        return libsvn_log_entry($rev, $author, $date, $msg);
 }
diff --git a/git.c b/git.c
index 102735af6c0cfb496a04593ba466a9fbb18f693d..7321d6c3f636ef8bd4870ead06be54c037ccef9b 100644 (file)
--- a/git.c
+++ b/git.c
@@ -35,6 +35,59 @@ static void prepend_to_path(const char *dir, int len)
        setenv("PATH", path, 1);
 }
 
+static int handle_options(const char*** argv, int* argc)
+{
+       int handled = 0;
+
+       while (*argc > 0) {
+               const char *cmd = (*argv)[0];
+               if (cmd[0] != '-')
+                       break;
+
+               /*
+                * For legacy reasons, the "version" and "help"
+                * commands can be written with "--" prepended
+                * to make them look like flags.
+                */
+               if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+                       break;
+
+               /*
+                * Check remaining flags.
+                */
+               if (!strncmp(cmd, "--exec-path", 11)) {
+                       cmd += 11;
+                       if (*cmd == '=')
+                               git_set_exec_path(cmd + 1);
+                       else {
+                               puts(git_exec_path());
+                               exit(0);
+                       }
+               } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+                       setup_pager();
+               } else if (!strcmp(cmd, "--git-dir")) {
+                       if (*argc < 1)
+                               return -1;
+                       setenv("GIT_DIR", (*argv)[1], 1);
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!strncmp(cmd, "--git-dir=", 10)) {
+                       setenv("GIT_DIR", cmd + 10, 1);
+               } else if (!strcmp(cmd, "--bare")) {
+                       static char git_dir[1024];
+                       setenv("GIT_DIR", getcwd(git_dir, 1024), 1);
+               } else {
+                       fprintf(stderr, "Unknown option: %s\n", cmd);
+                       cmd_usage(0, NULL, NULL);
+               }
+
+               (*argv)++;
+               (*argc)--;
+               handled++;
+       }
+       return handled;
+}
+
 static const char *alias_command;
 static char *alias_string = NULL;
 
@@ -103,49 +156,48 @@ static int handle_alias(int *argcp, const char ***argv)
 {
        int nongit = 0, ret = 0, saved_errno = errno;
        const char *subdir;
+       int count, option_count;
+       const char** new_argv;
 
        subdir = setup_git_directory_gently(&nongit);
-       if (!nongit) {
-               int count;
-               const char** new_argv;
-
-               alias_command = (*argv)[0];
-               git_config(git_alias_config);
-               if (alias_string) {
 
-                       count = split_cmdline(alias_string, &new_argv);
+       alias_command = (*argv)[0];
+       git_config(git_alias_config);
+       if (alias_string) {
+               count = split_cmdline(alias_string, &new_argv);
+               option_count = handle_options(&new_argv, &count);
+               memmove(new_argv - option_count, new_argv,
+                               count * sizeof(char *));
+               new_argv -= option_count;
 
-                       if (count < 1)
-                               die("empty alias for %s", alias_command);
+               if (count < 1)
+                       die("empty alias for %s", alias_command);
 
-                       if (!strcmp(alias_command, new_argv[0]))
-                               die("recursive alias: %s", alias_command);
+               if (!strcmp(alias_command, new_argv[0]))
+                       die("recursive alias: %s", alias_command);
 
-                       if (getenv("GIT_TRACE")) {
-                               int i;
-                               fprintf(stderr, "trace: alias expansion: %s =>",
-                                       alias_command);
-                               for (i = 0; i < count; ++i) {
-                                       fputc(' ', stderr);
-                                       sq_quote_print(stderr, new_argv[i]);
-                               }
-                               fputc('\n', stderr);
-                               fflush(stderr);
+               if (getenv("GIT_TRACE")) {
+                       int i;
+                       fprintf(stderr, "trace: alias expansion: %s =>",
+                               alias_command);
+                       for (i = 0; i < count; ++i) {
+                               fputc(' ', stderr);
+                               sq_quote_print(stderr, new_argv[i]);
                        }
+                       fputc('\n', stderr);
+                       fflush(stderr);
+               }
 
-                       /* insert after command name */
-                       if (*argcp > 1) {
-                               new_argv = realloc(new_argv, sizeof(char*) *
-                                                  (count + *argcp));
-                               memcpy(new_argv + count, *argv + 1,
-                                      sizeof(char*) * *argcp);
-                       }
+               new_argv = realloc(new_argv, sizeof(char*) *
+                                  (count + *argcp + 1));
+               /* insert after command name */
+               memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
+               new_argv[count+*argcp] = NULL;
 
-                       *argv = new_argv;
-                       *argcp += count - 1;
+               *argv = new_argv;
+               *argcp += count - 1;
 
-                       ret = 1;
-               }
+               ret = 1;
        }
 
        if (subdir)
@@ -158,51 +210,55 @@ static int handle_alias(int *argcp, const char ***argv)
 
 const char git_version_string[] = GIT_VERSION;
 
+#define NEEDS_PREFIX 1
+
 static void handle_internal_command(int argc, const char **argv, char **envp)
 {
        const char *cmd = argv[0];
        static struct cmd_struct {
                const char *cmd;
-               int (*fn)(int, const char **, char **);
+               int (*fn)(int, const char **, const char *);
+               int prefix;
        } commands[] = {
                { "version", cmd_version },
                { "help", cmd_help },
-               { "log", cmd_log },
-               { "whatchanged", cmd_whatchanged },
-               { "show", cmd_show },
+               { "log", cmd_log, NEEDS_PREFIX },
+               { "whatchanged", cmd_whatchanged, NEEDS_PREFIX },
+               { "show", cmd_show, NEEDS_PREFIX },
                { "push", cmd_push },
-               { "format-patch", cmd_format_patch },
+               { "format-patch", cmd_format_patch, NEEDS_PREFIX },
                { "count-objects", cmd_count_objects },
-               { "diff", cmd_diff },
-               { "grep", cmd_grep },
-               { "rm", cmd_rm },
-               { "add", cmd_add },
-               { "rev-list", cmd_rev_list },
+               { "diff", cmd_diff, NEEDS_PREFIX },
+               { "grep", cmd_grep, NEEDS_PREFIX },
+               { "rm", cmd_rm, NEEDS_PREFIX },
+               { "add", cmd_add, NEEDS_PREFIX },
+               { "rev-list", cmd_rev_list, NEEDS_PREFIX },
                { "init-db", cmd_init_db },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
                { "upload-tar", cmd_upload_tar },
                { "check-ref-format", cmd_check_ref_format },
-               { "ls-files", cmd_ls_files },
-               { "ls-tree", cmd_ls_tree },
-               { "tar-tree", cmd_tar_tree },
-               { "read-tree", cmd_read_tree },
-               { "commit-tree", cmd_commit_tree },
+               { "ls-files", cmd_ls_files, NEEDS_PREFIX },
+               { "ls-tree", cmd_ls_tree, NEEDS_PREFIX },
+               { "tar-tree", cmd_tar_tree, NEEDS_PREFIX },
+               { "read-tree", cmd_read_tree, NEEDS_PREFIX },
+               { "commit-tree", cmd_commit_tree, NEEDS_PREFIX },
                { "apply", cmd_apply },
-               { "show-branch", cmd_show_branch },
-               { "diff-files", cmd_diff_files },
-               { "diff-index", cmd_diff_index },
-               { "diff-stages", cmd_diff_stages },
-               { "diff-tree", cmd_diff_tree },
-               { "cat-file", cmd_cat_file },
-               { "rev-parse", cmd_rev_parse },
-               { "write-tree", cmd_write_tree },
+               { "show-branch", cmd_show_branch, NEEDS_PREFIX },
+               { "diff-files", cmd_diff_files, NEEDS_PREFIX },
+               { "diff-index", cmd_diff_index, NEEDS_PREFIX },
+               { "diff-stages", cmd_diff_stages, NEEDS_PREFIX },
+               { "diff-tree", cmd_diff_tree, NEEDS_PREFIX },
+               { "cat-file", cmd_cat_file, NEEDS_PREFIX },
+               { "rev-parse", cmd_rev_parse, NEEDS_PREFIX },
+               { "write-tree", cmd_write_tree, NEEDS_PREFIX },
                { "mailsplit", cmd_mailsplit },
                { "mailinfo", cmd_mailinfo },
                { "stripspace", cmd_stripspace },
-               { "update-index", cmd_update_index },
-               { "update-ref", cmd_update_ref },
-               { "fmt-merge-msg", cmd_fmt_merge_msg },
-               { "prune", cmd_prune },
+               { "update-index", cmd_update_index, NEEDS_PREFIX },
+               { "update-ref", cmd_update_ref, NEEDS_PREFIX },
+               { "fmt-merge-msg", cmd_fmt_merge_msg, NEEDS_PREFIX },
+               { "prune", cmd_prune, NEEDS_PREFIX },
+               { "mv", cmd_mv, NEEDS_PREFIX },
        };
        int i;
 
@@ -214,9 +270,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 
        for (i = 0; i < ARRAY_SIZE(commands); i++) {
                struct cmd_struct *p = commands+i;
+               const char *prefix;
                if (strcmp(p->cmd, cmd))
                        continue;
 
+               prefix = NULL;
+               if (p->prefix)
+                       prefix = setup_git_directory();
                if (getenv("GIT_TRACE")) {
                        int i;
                        fprintf(stderr, "trace: built-in: git");
@@ -228,7 +288,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                        fflush(stderr);
                }
 
-               exit(p->fn(argc, argv, envp));
+               exit(p->fn(argc, argv, prefix));
        }
 }
 
@@ -269,51 +329,19 @@ int main(int argc, const char **argv, char **envp)
                die("cannot handle %s internally", cmd);
        }
 
-       /* Default command: "help" */
-       cmd = "help";
-
        /* Look for flags.. */
-       while (argc > 1) {
-               cmd = *++argv;
-               argc--;
-
-               if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
-                       setup_pager();
-                       continue;
-               }
-
-               if (strncmp(cmd, "--", 2))
-                       break;
-
-               cmd += 2;
-
-               /*
-                * For legacy reasons, the "version" and "help"
-                * commands can be written with "--" prepended
-                * to make them look like flags.
-                */
-               if (!strcmp(cmd, "help"))
-                       break;
-               if (!strcmp(cmd, "version"))
-                       break;
-
-               /*
-                * Check remaining flags (which by now must be
-                * "--exec-path", but maybe we will accept
-                * other arguments some day)
-                */
-               if (!strncmp(cmd, "exec-path", 9)) {
-                       cmd += 9;
-                       if (*cmd == '=') {
-                               git_set_exec_path(cmd + 1);
-                               continue;
-                       }
-                       puts(git_exec_path());
-                       exit(0);
-               }
-               cmd_usage(0, NULL, NULL);
+       argv++;
+       argc--;
+       handle_options(&argv, &argc);
+       if (argc > 0) {
+               if (!strncmp(argv[0], "--", 2))
+                       argv[0] += 2;
+       } else {
+               /* Default command: "help" */
+               argv[0] = "help";
+               argc = 1;
        }
-       argv[0] = cmd;
+       cmd = argv[0];
 
        /*
         * We search for git commands in the following order:
index 2fd1e5f78ec8c2eb743c54cc0281821958141ce4..e5fca63b9c5ba8b525f4f7791e14a49d1f51399f 100755 (executable)
@@ -227,7 +227,7 @@ if (!defined $action || $action eq "summary") {
        git_tag();
        exit;
 } elsif ($action eq "blame") {
-       git_blame();
+       git_blame2();
        exit;
 } else {
        undef $action;
@@ -795,7 +795,7 @@ sub git_read_projects {
        if (-d $projects_list) {
                # search in directory
                my $dir = $projects_list;
-               opendir my $dh, $dir or return undef;
+               opendir my ($dh), $dir or return undef;
                while (my $dir = readdir($dh)) {
                        if (-e "$projectroot/$dir/HEAD") {
                                my $pr = {
@@ -810,7 +810,7 @@ sub git_read_projects {
                # 'git%2Fgit.git Linus+Torvalds'
                # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
                # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
-               open my $fd , $projects_list or return undef;
+               open my ($fd), $projects_list or return undef;
                while (my $line = <$fd>) {
                        chomp $line;
                        my ($path, $owner) = split ' ', $line;
@@ -1138,7 +1138,7 @@ sub git_summary {
                                      "</td>\n" .
                                      "<td>";
                                if (defined($comment)) {
-                                     print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
+                                     print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment));
                                }
                                print "</td>\n" .
                                      "<td class=\"link\">";
@@ -1199,6 +1199,20 @@ sub git_summary {
        git_footer_html();
 }
 
+sub git_print_page_path {
+       my $name = shift;
+       my $type = shift;
+
+       if (!defined $name) {
+               print "<div class=\"page_path\"><b>/</b></div>\n";
+       } elsif ($type =~ "blob") {
+               print "<div class=\"page_path\"><b>" .
+                       $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "</b><br/></div>\n";
+       } else {
+               print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
+       }
+}
+
 sub git_tag {
        my $head = git_read_head($project);
        git_header_html();
@@ -1238,6 +1252,73 @@ sub git_tag {
        git_footer_html();
 }
 
+sub git_blame2 {
+       my $fd;
+       my $ftype;
+       die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame'));
+       die_error('404 Not Found', "File name not defined") if (!$file_name);
+       $hash_base ||= git_read_head($project);
+       die_error(undef, "Reading commit failed") unless ($hash_base);
+       my %co = git_read_commit($hash_base)
+               or die_error(undef, "Reading commit failed");
+       if (!defined $hash) {
+               $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
+                       or die_error(undef, "Error looking up file");
+       }
+       $ftype = git_get_type($hash);
+       if ($ftype !~ "blob") {
+               die_error("400 Bad Request", "object is not a blob");
+       }
+       open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base)
+               or die_error(undef, "Open failed");
+       git_header_html();
+       print "<div class=\"page_nav\">\n" .
+               $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
+       print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
+               " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "<br/>\n";
+       print "</div>\n".
+               "<div>" .
+               $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
+               "</div>\n";
+       git_print_page_path($file_name, $ftype);
+       my @rev_color = (qw(light dark));
+       my $num_colors = scalar(@rev_color);
+       my $current_color = 0;
+       my $last_rev;
+       print "<div class=\"page_body\">\n";
+       print "<table class=\"blame\">\n";
+       print "<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n";
+       while (<$fd>) {
+               /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/;
+               my $full_rev = $1;
+               my $rev = substr($full_rev, 0, 8);
+               my $lineno = $2;
+               my $data = $3;
+
+               if (!defined $last_rev) {
+                       $last_rev = $full_rev;
+               } elsif ($last_rev ne $full_rev) {
+                       $last_rev = $full_rev;
+                       $current_color = ++$current_color % $num_colors;
+               }
+               print "<tr class=\"$rev_color[$current_color]\">\n";
+               print "<td class=\"sha1\">" .
+                       $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "</td>\n";
+               print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" . esc_html($lineno) . "</a></td>\n";
+               print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+               print "</tr>\n";
+       }
+       print "</table>\n";
+       print "</div>";
+       close $fd or print "Reading blob failed\n";
+       git_footer_html();
+}
+
 sub git_blame {
        my $fd;
        die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame'));
@@ -1266,7 +1347,7 @@ sub git_blame {
                "<div>" .
                $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
                "</div>\n";
-       print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
+       git_print_page_path($file_name);
        print "<div class=\"page_body\">\n";
        print <<HTML;
 <table class="blame">
@@ -1531,6 +1612,14 @@ sub git_blob_plain_mimetype {
 }
 
 sub git_blob_plain {
+       if (!defined $hash) {
+                if (defined $file_name) {
+                        my $base = $hash_base || git_read_head($project);
+                        $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+                } else {
+                        die_error(undef, "No file name defined.");
+                }
+        }
        my $type = shift;
        open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
 
@@ -1554,10 +1643,14 @@ sub git_blob_plain {
 }
 
 sub git_blob {
-       if (!defined $hash && defined $file_name) {
-               my $base = $hash_base || git_read_head($project);
-               $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
-       }
+       if (!defined $hash) {
+                if (defined $file_name) {
+                        my $base = $hash_base || git_read_head($project);
+                        $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+                } else {
+                        die_error(undef, "No file name defined.");
+                }
+        }
        my $have_blame = git_get_project_config_bool ('blame');
        open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed.");
        my $mimetype = git_blob_plain_mimetype($fd, $file_name);
@@ -1592,9 +1685,7 @@ sub git_blob {
                      "<br/><br/></div>\n" .
                      "<div class=\"title\">$hash</div>\n";
        }
-       if (defined $file_name) {
-               print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
-       }
+       git_print_page_path($file_name, "blob");
        print "<div class=\"page_body\">\n";
        my $nr;
        while (my $line = <$fd>) {
@@ -1659,10 +1750,8 @@ sub git_tree {
        }
        if (defined $file_name) {
                $base = esc_html("$file_name/");
-               print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
-       } else {
-               print "<div class=\"page_path\"><b>/</b></div>\n";
        }
+       git_print_page_path($file_name);
        print "<div class=\"page_body\">\n";
        print "<table cellspacing=\"0\">\n";
        my $alternate = 0;
@@ -1687,7 +1776,7 @@ sub git_tree {
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") .
 #                            " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") .
-                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
+                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") .
                              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") .
                              "</td>\n";
                } elsif ($t_type eq "tree") {
@@ -1696,7 +1785,7 @@ sub git_tree {
                              "</td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") .
-                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
+                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash_base;f=$base$t_name")}, "history") .
                              "</td>\n";
                }
                print "</tr>\n";
@@ -1931,7 +2020,13 @@ sub git_commit {
                print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff");
        }
        print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "\n" .
-             "<br/><br/></div>\n";
+               "<br/>\n";
+       if (defined $file_name && defined $co{'parent'}) {
+               my $parent = $co{'parent'};
+               print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame") . "\n";
+       }
+       print "<br/></div>\n";
+
        if (defined $co{'parent'}) {
                print "<div>\n" .
                      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
@@ -2041,7 +2136,7 @@ sub git_commit {
                              "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
-                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
+                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") .
                              "</td>\n"
                } elsif ($status eq "M" || $status eq "T") {
                        my $mode_chnge = "";
@@ -2072,7 +2167,7 @@ sub git_commit {
                        if ($to_id ne $from_id) {
                                print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff");
                        }
-                       print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") . "\n";
+                       print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n";
                        print "</td>\n";
                } elsif ($status eq "R") {
                        my ($from_file, $to_file) = split "\t", $file;
@@ -2120,9 +2215,7 @@ sub git_blobdiff {
                      "<br/><br/></div>\n" .
                      "<div class=\"title\">$hash vs $hash_parent</div>\n";
        }
-       if (defined $file_name) {
-               print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
-       }
+       git_print_page_path($file_name, "blob");
        print "<div class=\"page_body\">\n" .
              "<div class=\"diff_info\">blob:" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) .
@@ -2293,10 +2386,11 @@ sub git_commitdiff_plain {
 }
 
 sub git_history {
-       if (!defined $hash) {
-               $hash = git_read_head($project);
+       if (!defined $hash_base) {
+               $hash_base = git_read_head($project);
        }
-       my %co = git_read_commit($hash);
+       my $ftype;
+       my %co = git_read_commit($hash_base);
        if (!%co) {
                die_error(undef, "Unknown commit object.");
        }
@@ -2306,18 +2400,24 @@ sub git_history {
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
-             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
-             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
-             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
+             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
              "<br/><br/>\n" .
              "</div>\n";
        print "<div>\n" .
-             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
              "</div>\n";
-       print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
+       if (!defined $hash && defined $file_name) {
+               $hash = git_get_hash_by_path($hash_base, $file_name);
+       }
+       if (defined $hash) {
+               $ftype = git_get_type($hash);
+       }
+       git_print_page_path($file_name, $ftype);
 
        open my $fd, "-|",
-               "$GIT rev-list --full-history $hash -- \'$file_name\'";
+               "$GIT rev-list --full-history $hash_base -- \'$file_name\'";
        print "<table cellspacing=\"0\">\n";
        my $alternate = 0;
        while (my $line = <$fd>) {
@@ -2345,7 +2445,7 @@ sub git_history {
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
                              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
                              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob");
-                       my $blob = git_get_hash_by_path($hash, $file_name);
+                       my $blob = git_get_hash_by_path($hash_base, $file_name);
                        my $blob_parent = git_get_hash_by_path($commit, $file_name);
                        if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
                                print " | " .
index 12493fbed2822ce41b5b4c23fb85d6f4a47c104a..1aad39b4d830d32b9ac4034153a06ddc08c72bcb 100644 (file)
@@ -36,6 +36,8 @@ enum XML_Status {
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
+static int commits_on_stdin = 0;
+
 static int got_alternates = -1;
 static int corrupt_object_found = 0;
 
@@ -43,7 +45,7 @@ static struct curl_slist *no_pragma_header;
 
 struct alt_base
 {
-       char *base;
+       const char *base;
        int path_len;
        int got_indices;
        struct packed_git *packs;
@@ -81,7 +83,7 @@ struct object_request
 };
 
 struct alternates_request {
-       char *base;
+       const char *base;
        char *url;
        struct buffer *buffer;
        struct active_request_slot *slot;
@@ -142,7 +144,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        return size;
 }
 
-static void fetch_alternates(char *base);
+static void fetch_alternates(const char *base);
 
 static void process_object_response(void *callback_data);
 
@@ -507,7 +509,7 @@ static void process_alternates_response(void *callback_data)
                (struct alternates_request *)callback_data;
        struct active_request_slot *slot = alt_req->slot;
        struct alt_base *tail = alt;
-       char *base = alt_req->base;
+       const char *base = alt_req->base;
        static const char null_byte = '\0';
        char *data;
        int i = 0;
@@ -612,7 +614,7 @@ static void process_alternates_response(void *callback_data)
        got_alternates = 1;
 }
 
-static void fetch_alternates(char *base)
+static void fetch_alternates(const char *base)
 {
        struct buffer buffer;
        char *url;
@@ -1185,7 +1187,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
         char *url;
         char hex[42];
         struct buffer buffer;
-       char *base = alt->base;
+       const char *base = alt->base;
        struct active_request_slot *slot;
        struct slot_results results;
         buffer.size = 41;
@@ -1214,10 +1216,12 @@ int fetch_ref(char *ref, unsigned char *sha1)
         return 0;
 }
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-       char *commit_id;
-       char *url;
+       int commits;
+       const char **write_ref = NULL;
+       char **commit_id;
+       const char *url;
        char *path;
        int arg = 1;
        int rc = 0;
@@ -1237,20 +1241,26 @@ int main(int argc, char **argv)
                } else if (argv[arg][1] == 'v') {
                        get_verbosely = 1;
                } else if (argv[arg][1] == 'w') {
-                       write_ref = argv[arg + 1];
+                       write_ref = &argv[arg + 1];
                        arg++;
                } else if (!strcmp(argv[arg], "--recover")) {
                        get_recover = 1;
+               } else if (!strcmp(argv[arg], "--stdin")) {
+                       commits_on_stdin = 1;
                }
                arg++;
        }
-       if (argc < arg + 2) {
-               usage("git-http-fetch [-c] [-t] [-a] [-d] [-v] [--recover] [-w ref] commit-id url");
+       if (argc < arg + 2 - commits_on_stdin) {
+               usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
                return 1;
        }
-       commit_id = argv[arg];
-       url = argv[arg + 1];
-       write_ref_log_details = url;
+       if (commits_on_stdin) {
+               commits = pull_targets_stdin(&commit_id, &write_ref);
+       } else {
+               commit_id = (char **) &argv[arg++];
+               commits = 1;
+       }
+       url = argv[arg];
 
        http_init();
 
@@ -1268,13 +1278,16 @@ int main(int argc, char **argv)
                        alt->path_len = strlen(path);
        }
 
-       if (pull(commit_id))
+       if (pull(commits, commit_id, write_ref, url))
                rc = 1;
 
        http_cleanup();
 
        curl_slist_free_all(no_pragma_header);
 
+       if (commits_on_stdin)
+               pull_targets_free(commits, commit_id, write_ref);
+
        if (corrupt_object_found) {
                fprintf(stderr,
 "Some loose object were found to be corrupt, but they might be just\n"
index 47686195cdf41ad58fe66a606033d3156de8fd82..4021e7d9274b713a7b5c48d66f9ddf21ddc07094 100644 (file)
@@ -2521,7 +2521,7 @@ int main(int argc, char **argv)
                        commit_argv[3] = old_sha1_hex;
                        commit_argc++;
                }
-               init_revisions(&revs);
+               init_revisions(&revs, setup_git_directory());
                setup_revisions(commit_argc, commit_argv, &revs, NULL);
                free(new_sha1_hex);
                if (old_sha1_hex) {
index ffa4887570319a2cbcf4042bd6e00063a48a4b6d..b216bdd55742ef8214ed1193ef184f8bba70eb70 100644 (file)
@@ -8,8 +8,9 @@
 static int use_link = 0;
 static int use_symlink = 0;
 static int use_filecopy = 1;
+static int commits_on_stdin = 0;
 
-static char *path; /* "Remote" git repository */
+static const char *path; /* "Remote" git repository */
 
 void prefetch(unsigned char *sha1)
 {
@@ -194,17 +195,19 @@ int fetch_ref(char *ref, unsigned char *sha1)
 }
 
 static const char local_pull_usage[] =
-"git-local-fetch [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] commit-id path";
+"git-local-fetch [-c] [-t] [-a] [-v] [-w filename] [--recover] [-l] [-s] [-n] [--stdin] commit-id path";
 
-/* 
+/*
  * By default we only use file copy.
  * If -l is specified, a hard link is attempted.
  * If -s is specified, then a symlink is attempted.
  * If -n is _not_ specified, then a regular file-to-file copy is done.
  */
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-       char *commit_id;
+       int commits;
+       const char **write_ref = NULL;
+       char **commit_id;
        int arg = 1;
 
        setup_git_directory();
@@ -229,21 +232,30 @@ int main(int argc, char **argv)
                else if (argv[arg][1] == 'v')
                        get_verbosely = 1;
                else if (argv[arg][1] == 'w')
-                       write_ref = argv[++arg];
+                       write_ref = &argv[++arg];
                else if (!strcmp(argv[arg], "--recover"))
                        get_recover = 1;
+               else if (!strcmp(argv[arg], "--stdin"))
+                       commits_on_stdin = 1;
                else
                        usage(local_pull_usage);
                arg++;
        }
-       if (argc < arg + 2)
+       if (argc < arg + 2 - commits_on_stdin)
                usage(local_pull_usage);
-       commit_id = argv[arg];
-       path = argv[arg + 1];
-       write_ref_log_details = path;
+       if (commits_on_stdin) {
+               commits = pull_targets_stdin(&commit_id, &write_ref);
+       } else {
+               commit_id = (char **) &argv[arg++];
+               commits = 1;
+       }
+       path = argv[arg];
 
-       if (pull(commit_id))
+       if (pull(commits, commit_id, write_ref, path))
                return 1;
 
+       if (commits_on_stdin)
+               pull_targets_free(commits, commit_id, write_ref);
+
        return 0;
 }
index 9d8d46fa0038202bd49dee9cf5d1a60f781e5d7d..b67b8dd17af9643e00152d9bd768dca89e08b4ce 100644 (file)
@@ -97,6 +97,11 @@ void show_log(struct rev_info *opt, const char *sep)
                        subject = "Subject: ";
 
                printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
+               if (opt->message_id)
+                       printf("Message-Id: <%s>\n", opt->message_id);
+               if (opt->ref_message_id)
+                       printf("In-Reply-To: <%s>\nReferences: <%s>\n",
+                              opt->ref_message_id, opt->ref_message_id);
                if (opt->mime_boundary) {
                        static char subject_buffer[1024];
                        static char buffer[1024];
@@ -129,7 +134,8 @@ void show_log(struct rev_info *opt, const char *sep)
                        opt->diffopt.stat_sep = buffer;
                }
        } else {
-               printf("%s%s",
+               printf("%s%s%s",
+                      diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
                       opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
                       diff_unique_abbrev(commit->object.sha1, abbrev_commit));
                if (opt->parents)
@@ -138,6 +144,8 @@ void show_log(struct rev_info *opt, const char *sep)
                        printf(" (from %s)",
                               diff_unique_abbrev(parent->object.sha1,
                                                  abbrev_commit));
+               printf("%s",
+                      diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
                putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
        }
 
index 04a48b925b4db0e302650f7383b5ccbb948665de..861c7f08ff1b68eee141442411b4706a1bcc2518 100644 (file)
@@ -63,6 +63,7 @@ static const char *base_name;
 static unsigned char pack_file_sha1[20];
 static int progress = 1;
 static volatile sig_atomic_t progress_update = 0;
+static int window = 10;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -1216,16 +1217,26 @@ static void setup_progress_signal(void)
        setitimer(ITIMER_REAL, &v, NULL);
 }
 
+static int git_pack_config(const char *k, const char *v)
+{
+       if(!strcmp(k, "pack.window")) {
+               window = git_config_int(k, v);
+               return 0;
+       }
+       return git_default_config(k, v);
+}
+
 int main(int argc, char **argv)
 {
        SHA_CTX ctx;
        char line[40 + 1 + PATH_MAX + 2];
-       int window = 10, depth = 10, pack_to_stdout = 0;
+       int depth = 10, pack_to_stdout = 0;
        struct object_entry **list;
        int num_preferred_base = 0;
        int i;
 
        setup_git_directory();
+       git_config(git_pack_config);
 
        progress = isatty(2);
        for (i = 1; i < argc; i++) {
index 9c0a9fc2a1d8cba09e6f695883bf74b40243e99c..c375e912a957114fa3473383c6318de185c8aa79 100644 (file)
@@ -319,6 +319,45 @@ int remove_file_from_cache(const char *path)
        return 0;
 }
 
+int add_file_to_index(const char *path, int verbose)
+{
+       int size, namelen;
+       struct stat st;
+       struct cache_entry *ce;
+
+       if (lstat(path, &st))
+               die("%s: unable to stat (%s)", path, strerror(errno));
+
+       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+               die("%s: can only add regular files or symbolic links", path);
+
+       namelen = strlen(path);
+       size = cache_entry_size(namelen);
+       ce = xcalloc(1, size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_flags = htons(namelen);
+       fill_stat_cache_info(ce, &st);
+
+       ce->ce_mode = create_ce_mode(st.st_mode);
+       if (!trust_executable_bit) {
+               /* If there is an existing entry, pick the mode bits
+                * from it.
+                */
+               int pos = cache_name_pos(path, namelen);
+               if (pos >= 0)
+                       ce->ce_mode = active_cache[pos]->ce_mode;
+       }
+
+       if (index_path(ce->sha1, path, &st, 1))
+               die("unable to index file %s", path);
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+               die("unable to add %s to index",path);
+       if (verbose)
+               printf("add '%s'\n", path);
+       cache_tree_invalidate_path(active_cache_tree, path);
+       return 0;
+}
+
 int ce_same_name(struct cache_entry *a, struct cache_entry *b)
 {
        int len = ce_namelen(a);
diff --git a/refs.c b/refs.c
index 56db394459cec2cd156ee2243a1dbac33867f926..02850b69083f793becacc59a0da97a8f69367e4e 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -294,6 +294,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path,
        int plen,
        const unsigned char *old_sha1, int mustexist)
 {
+       const char *orig_path = path;
        struct ref_lock *lock;
        struct stat st;
 
@@ -303,7 +304,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path,
        plen = strlen(path) - plen;
        path = resolve_ref(path, lock->old_sha1, mustexist);
        if (!path) {
+               int last_errno = errno;
+               error("unable to resolve reference %s: %s",
+                       orig_path, strerror(errno));
                unlock_ref(lock);
+               errno = last_errno;
                return NULL;
        }
        lock->lk = xcalloc(1, sizeof(struct lock_file));
index 874e349db818d52329e30f2634855e5cf14192b8..a58257ad8037f7009c69ee0502c3669756bb3987 100644 (file)
@@ -509,7 +509,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
        return 1;
 }
 
-void init_revisions(struct rev_info *revs)
+void init_revisions(struct rev_info *revs, const char *prefix)
 {
        memset(revs, 0, sizeof(*revs));
 
@@ -521,7 +521,7 @@ void init_revisions(struct rev_info *revs)
        revs->pruning.change = file_change;
        revs->lifo = 1;
        revs->dense = 1;
-       revs->prefix = setup_git_directory();
+       revs->prefix = prefix;
        revs->max_age = -1;
        revs->min_age = -1;
        revs->max_count = -1;
index c010a0811604e55f4e46d08d986ae62edfb192ee..0c3b8d99059fb272b913a85c2873f5fef64acc00 100644 (file)
@@ -61,6 +61,8 @@ struct rev_info {
        struct log_info *loginfo;
        int             nr, total;
        const char      *mime_boundary;
+       const char      *message_id;
+       const char      *ref_message_id;
        const char      *add_signoff;
        const char      *extra_headers;
 
@@ -85,7 +87,7 @@ struct rev_info {
 extern int rev_same_tree_as_empty(struct rev_info *, struct tree *t1);
 extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);
 
-extern void init_revisions(struct rev_info *revs);
+extern void init_revisions(struct rev_info *revs, const char *prefix);
 extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
 extern void prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
diff --git a/setup.c b/setup.c
index 4612f110ee0beaeada3cb31ce7d47ad7175eec9c..358e139d84c422a9bd0a9500807a31cbf7695f02 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -184,6 +184,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                return NULL;
        bad_dir_environ:
+               if (!nongit_ok) {
+                       *nongit_ok = 1;
+                       return NULL;
+               }
                path[len] = 0;
                die("Not a git repository: '%s'", path);
        }
index 8734d501fec50dc16b298e1473749c64ec25fe81..43bc2ea0cf039bb9fd02c8313981e85bd7398d33 100644 (file)
@@ -684,26 +684,74 @@ static void *map_sha1_file_internal(const unsigned char *sha1,
        return map;
 }
 
-static int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size)
+static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
+       unsigned char c;
+       unsigned int word, bits;
+       unsigned long size;
+       static const char *typename[8] = {
+               NULL,   /* OBJ_EXT */
+               "commit", "tree", "blob", "tag",
+               NULL, NULL, NULL
+       };
+       const char *type;
+
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
        stream->next_in = map;
        stream->avail_in = mapsize;
        stream->next_out = buffer;
-       stream->avail_out = size;
+       stream->avail_out = bufsiz;
+
+       /*
+        * Is it a zlib-compressed buffer? If so, the first byte
+        * must be 0x78 (15-bit window size, deflated), and the
+        * first 16-bit word is evenly divisible by 31
+        */
+       word = (map[0] << 8) + map[1];
+       if (map[0] == 0x78 && !(word % 31)) {
+               inflateInit(stream);
+               return inflate(stream, 0);
+       }
+
+       c = *map++;
+       mapsize--;
+       type = typename[(c >> 4) & 7];
+       if (!type)
+               return -1;
 
+       bits = 4;
+       size = c & 0xf;
+       while ((c & 0x80)) {
+               if (bits >= 8*sizeof(long))
+                       return -1;
+               c = *map++;
+               size += (c & 0x7f) << bits;
+               bits += 7;
+               mapsize--;
+       }
+
+       /* Set up the stream for the rest.. */
+       stream->next_in = map;
+       stream->avail_in = mapsize;
        inflateInit(stream);
-       return inflate(stream, 0);
+
+       /* And generate the fake traditional header */
+       stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
+       return 0;
 }
 
 static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
 {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
+       unsigned long n;
 
-       memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
-       bytes = stream->total_out - bytes;
+       n = stream->total_out - bytes;
+       if (n > size)
+               n = size;
+       memcpy(buf, (char *) buffer + bytes, n);
+       bytes = n;
        if (bytes < size) {
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
@@ -1331,31 +1379,29 @@ char *write_sha1_file_prepare(void *buf,
 static int link_temp_to_file(const char *tmpfile, char *filename)
 {
        int ret;
+       char *dir;
 
        if (!link(tmpfile, filename))
                return 0;
 
        /*
-        * Try to mkdir the last path component if that failed
-        * with an ENOENT.
+        * Try to mkdir the last path component if that failed.
         *
         * 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);
-                       if (adjust_shared_perm(filename))
-                               return -2;
-                       *dir = '/';
-                       if (!link(tmpfile, filename))
-                               return 0;
-                       ret = errno;
-               }
+       dir = strrchr(filename, '/');
+       if (dir) {
+               *dir = 0;
+               mkdir(filename, 0777);
+               if (adjust_shared_perm(filename))
+                       return -2;
+               *dir = '/';
+               if (!link(tmpfile, filename))
+                       return 0;
+               ret = errno;
        }
        return ret;
 }
@@ -1414,6 +1460,49 @@ static int write_buffer(int fd, const void *buf, size_t len)
        return 0;
 }
 
+static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
+{
+       int hdr_len;
+       unsigned char c;
+
+       c = (type << 4) | (len & 15);
+       len >>= 4;
+       hdr_len = 1;
+       while (len) {
+               *hdr++ = c | 0x80;
+               hdr_len++;
+               c = (len & 0x7f);
+               len >>= 7;
+       }
+       *hdr = c;
+       return hdr_len;
+}
+
+static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
+{
+       int obj_type, hdr;
+
+       if (use_legacy_headers) {
+               while (deflate(stream, 0) == Z_OK)
+                       /* nothing */;
+               return;
+       }
+       if (!strcmp(type, blob_type))
+               obj_type = OBJ_BLOB;
+       else if (!strcmp(type, tree_type))
+               obj_type = OBJ_TREE;
+       else if (!strcmp(type, commit_type))
+               obj_type = OBJ_COMMIT;
+       else if (!strcmp(type, tag_type))
+               obj_type = OBJ_TAG;
+       else
+               die("trying to generate bogus object of type '%s'", type);
+       hdr = write_binary_header(stream->next_out, obj_type, len);
+       stream->total_out = hdr;
+       stream->next_out += hdr;
+       stream->avail_out -= hdr;
+}
+
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
        int size;
@@ -1459,7 +1548,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
-       size = deflateBound(&stream, len+hdrlen);
+       size = 8 + deflateBound(&stream, len+hdrlen);
        compressed = xmalloc(size);
 
        /* Compress it */
@@ -1469,8 +1558,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* First header.. */
        stream.next_in = hdr;
        stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
+       setup_object_header(&stream, type, len);
 
        /* Then the data itself.. */
        stream.next_in = buf;
index 28f7fd9174e8c48493983b17fa7b18cdecd41609..6e16568f88f6fcbbb79d23c1f6187860c8cfb453 100644 (file)
@@ -120,9 +120,10 @@ int fetch_ref(char *ref, unsigned char *sha1)
 
 static const char ssh_fetch_usage[] =
   MY_PROGRAM_NAME
-  " [-c] [-t] [-a] [-v] [-d] [--recover] [-w ref] commit-id url";
+  " [-c] [-t] [-a] [-v] [--recover] [-w ref] commit-id url";
 int main(int argc, char **argv)
 {
+       const char *write_ref = NULL;
        char *commit_id;
        char *url;
        int arg = 1;
@@ -159,7 +160,6 @@ int main(int argc, char **argv)
        }
        commit_id = argv[arg];
        url = argv[arg + 1];
-       write_ref_log_details = url;
 
        if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
                return 1;
@@ -167,7 +167,7 @@ int main(int argc, char **argv)
        if (get_version())
                return 1;
 
-       if (pull(commit_id))
+       if (pull(1, &commit_id, &write_ref, url))
                return 1;
 
        return 0;
index 04fab26621af3aee3a44913ab290a4893b4f23ca..ddc80bbeae7703b8c4a2ddcf12d8b1ec9c6bbe46 100755 (executable)
@@ -14,6 +14,8 @@ D=4444444444444444444444444444444444444444
 E=5555555555555555555555555555555555555555
 F=6666666666666666666666666666666666666666
 m=refs/heads/master
+n_dir=refs/heads/gu
+n=$n_dir/fixes
 
 test_expect_success \
        "create $m" \
@@ -25,6 +27,16 @@ test_expect_success \
         test $B = $(cat .git/$m)'
 rm -f .git/$m
 
+test_expect_success \
+       "fail to create $n" \
+       'touch .git/$n_dir
+        git-update-ref $n $A >out 2>err
+        test $? = 1 &&
+        test "" = "$(cat out)" &&
+        grep "error: unable to resolve reference" err &&
+        grep $n err'
+rm -f .git/$n_dir out err
+
 test_expect_success \
        "create $m (by HEAD)" \
        'git-update-ref HEAD $A &&
index fbb508d389e078263d983bf6736df8f1a0fe7b5d..22da6a00ccbe910d796dc84666caa2acfa9b8389 100755 (executable)
@@ -13,8 +13,8 @@ test_description='git-apply handling copy/rename patch.
 cat >test-patch <<\EOF
 diff --git a/foo b/bar
 similarity index 47%
-copy from foo
-copy to bar
+rename from foo
+rename to bar
 --- a/foo
 +++ b/bar
 @@ -1 +1 @@
@@ -39,4 +39,24 @@ else
            'test -f bar && ls -l bar | grep "^-..x......"'
 fi
 
+test_expect_success 'apply reverse' \
+    'git-apply -R --index --stat --summary --apply test-patch &&
+     test "$(cat foo)" = "This is foo"'
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+copy from foo
+copy to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+test_expect_success 'apply copy' \
+    'git-apply --index --stat --summary --apply test-patch &&
+     test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
+
 test_done
index 00bd8b15c625f3f95aefa771abded1a985342c23..ff052699a28d2d96de4bdaa4702f175527f28536 100755 (executable)
@@ -35,8 +35,8 @@ git-commit -m 'Second Version'
 git-diff-tree -p master binary >B.diff
 git-diff-tree -p -C master binary >C.diff
 
-git-diff-tree -p --full-index master binary >BF.diff
-git-diff-tree -p --full-index -C master binary >CF.diff
+git-diff-tree -p --binary master binary >BF.diff
+git-diff-tree -p --binary -C master binary >CF.diff
 
 test_expect_success 'stat binary diff -- should not fail.' \
        'git-checkout master
index a06f6956d5c72cae79129fc01a1be2f30121e6fb..69e9603c784b580ff143e4249c72aea79f64f471 100755 (executable)
@@ -11,31 +11,7 @@ test_description='git-apply should not get confused with rename/copy.
 
 # setup
 
-mkdir -p include/arch/x86_64/klibc klibc/arch/x86_64/include/klibc
-
-cat >include/arch/x86_64/klibc/archsetjmp.h <<\EOF
-/*
- * arch/x86_64/include/klibc/archsetjmp.h
- */
-
-#ifndef _KLIBC_ARCHSETJMP_H
-#define _KLIBC_ARCHSETJMP_H
-
-struct __jmp_buf {
-  unsigned long __rbx;
-  unsigned long __rsp;
-  unsigned long __rbp;
-  unsigned long __r12;
-  unsigned long __r13;
-  unsigned long __r14;
-  unsigned long __r15;
-  unsigned long __rip;
-};
-
-typedef struct __jmp_buf jmp_buf[1];
-
-#endif /* _SETJMP_H */
-EOF
+mkdir -p klibc/arch/x86_64/include/klibc
 
 cat >klibc/arch/x86_64/include/klibc/archsetjmp.h <<\EOF
 /*
@@ -139,7 +115,7 @@ rename to include/arch/m32r/klibc/archsetjmp.h
 +#endif /* _KLIBC_ARCHSETJMP_H */
 EOF
 
-find include klibc -type f -print | xargs git-update-index --add --
+find klibc -type f -print | xargs git-update-index --add --
 
 test_expect_success 'check rename/copy patch' 'git-apply --check patch'
 
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
new file mode 100755 (executable)
index 0000000..ca81d72
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git-apply should not get confused with type changes.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup repository and commits' '
+       echo "hello world" > foo &&
+       echo "hi planet" > bar &&
+       git update-index --add foo bar &&
+       git commit -m initial &&
+       git branch initial &&
+       rm -f foo &&
+       ln -s bar foo &&
+       git update-index foo &&
+       git commit -m "foo symlinked to bar" &&
+       git branch foo-symlinked-to-bar &&
+       rm -f foo &&
+       echo "how far is the sun?" > foo &&
+       git update-index foo &&
+       git commit -m "foo back to file" &&
+       git branch foo-back-to-file &&
+       rm -f foo &&
+       git update-index --remove foo &&
+       mkdir foo &&
+       echo "if only I knew" > foo/baz &&
+       git update-index --add foo/baz &&
+       git commit -m "foo becomes a directory" &&
+       git branch "foo-becomes-a-directory" &&
+       echo "hello world" > foo/baz &&
+       git update-index foo/baz &&
+       git commit -m "foo/baz is the original foo" &&
+       git branch foo-baz-renamed-from-foo
+       '
+
+test_expect_success 'file renamed from foo to foo/baz' '
+       git checkout -f initial &&
+       git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'file renamed from foo/baz to foo' '
+       git checkout -f foo-baz-renamed-from-foo &&
+       git diff-tree -M -p HEAD initial > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes file' '
+       git checkout -f foo-becomes-a-directory &&
+       git diff-tree -p HEAD initial > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes directory' '
+       git checkout -f initial &&
+       git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes symlink' '
+       git checkout -f initial &&
+       git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes file' '
+       git checkout -f foo-symlinked-to-bar &&
+       git diff-tree -p HEAD foo-back-to-file > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes directory' '
+       git checkout -f foo-symlinked-to-bar &&
+       git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes symlink' '
+       git checkout -f foo-becomes-a-directory &&
+       git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+       git apply --index < patch
+       '
+test_debug 'cat patch'
+
+
+test_done
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
new file mode 100755 (executable)
index 0000000..5182dbb
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='git-rev-list trivial path optimization test'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+echo Hello > a &&
+git add a &&
+git commit -m "Initial commit" a
+'
+
+test_expect_success path-optimization '
+    commit=$(echo "Unchanged tree" | git-commit-tree "HEAD^{tree}" -p HEAD) &&
+    test $(git-rev-list $commit | wc -l) = 2 &&
+    test $(git-rev-list $commit -- . | wc -l) = 1
+'
+
+test_done
index 811a4797a58dbc3411f885535b90e6df99f06334..900ca93cde02f82c35a59ee99349f9d0c4213867 100755 (executable)
@@ -38,4 +38,44 @@ test_expect_success \
     'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
     grep -E "^R100.+path1/COPYING.+path0/COPYING"'
 
+test_expect_success \
+    'adding another file' \
+    'cp ../../README path0/README &&
+     git-add path0/README &&
+     git-commit -m add2 -a'
+
+test_expect_success \
+    'moving whole subdirectory' \
+    'git-mv path0 path2'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path0/COPYING.+path2/COPYING" &&
+     git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path0/README.+path2/README"'
+
+test_expect_success \
+    'moving whole subdirectory into subdirectory' \
+    'git-mv path2 path1'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path2/COPYING.+path1/path2/COPYING" &&
+     git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path2/README.+path1/path2/README"'
+
+test_expect_failure \
+    'do not move directory over existing directory' \
+    'mkdir path0 && mkdir path0/path2 && git-mv path2 path0'
+
 test_done
index 3b824b04a262b92808ba138b1922badbbb25da89..48c1ee7968cc6b40cb02cd99d35e66630f922041 100644 (file)
@@ -241,11 +241,6 @@ static void unpack_one(unsigned nr, unsigned total)
        }
 }
 
-/*
- * We unpack from the end, older files first. Now, usually
- * there are deltas etc, so we'll not actually write the
- * objects in that order, but we might as well try..
- */
 static void unpack_all(void)
 {
        int i;
diff --git a/unpack-trees.c b/unpack-trees.c
new file mode 100644 (file)
index 0000000..a20639b
--- /dev/null
@@ -0,0 +1,799 @@
+#include <signal.h>
+#include <sys/time.h>
+#include "cache.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+
+#define DBRT_DEBUG 1
+
+struct tree_entry_list {
+       struct tree_entry_list *next;
+       unsigned directory : 1;
+       unsigned executable : 1;
+       unsigned symlink : 1;
+       unsigned int mode;
+       const char *name;
+       const unsigned char *sha1;
+};
+
+static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
+{
+       struct tree_desc desc;
+       struct name_entry one;
+       struct tree_entry_list *ret = NULL;
+       struct tree_entry_list **list_p = &ret;
+
+       if (!tree->object.parsed)
+               parse_tree(tree);
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+
+       while (tree_entry(&desc, &one)) {
+               struct tree_entry_list *entry;
+
+               entry = xmalloc(sizeof(struct tree_entry_list));
+               entry->name = one.path;
+               entry->sha1 = one.sha1;
+               entry->mode = one.mode;
+               entry->directory = S_ISDIR(one.mode) != 0;
+               entry->executable = (one.mode & S_IXUSR) != 0;
+               entry->symlink = S_ISLNK(one.mode) != 0;
+               entry->next = NULL;
+
+               *list_p = entry;
+               list_p = &entry->next;
+       }
+       return ret;
+}
+
+static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
+{
+       int len1 = strlen(name1);
+       int len2 = strlen(name2);
+       int len = len1 < len2 ? len1 : len2;
+       int ret = memcmp(name1, name2, len);
+       unsigned char c1, c2;
+       if (ret)
+               return ret;
+       c1 = name1[len];
+       c2 = name2[len];
+       if (!c1 && dir1)
+               c1 = '/';
+       if (!c2 && dir2)
+               c2 = '/';
+       ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+       if (c1 && c2 && !ret)
+               ret = len1 - len2;
+       return ret;
+}
+
+static int unpack_trees_rec(struct tree_entry_list **posns, int len,
+                           const char *base, struct unpack_trees_options *o,
+                           int *indpos,
+                           struct tree_entry_list *df_conflict_list)
+{
+       int baselen = strlen(base);
+       int src_size = len + 1;
+       do {
+               int i;
+               const char *first;
+               int firstdir = 0;
+               int pathlen;
+               unsigned ce_size;
+               struct tree_entry_list **subposns;
+               struct cache_entry **src;
+               int any_files = 0;
+               int any_dirs = 0;
+               char *cache_name;
+               int ce_stage;
+
+               /* Find the first name in the input. */
+
+               first = NULL;
+               cache_name = NULL;
+
+               /* Check the cache */
+               if (o->merge && *indpos < active_nr) {
+                       /* This is a bit tricky: */
+                       /* If the index has a subdirectory (with
+                        * contents) as the first name, it'll get a
+                        * filename like "foo/bar". But that's after
+                        * "foo", so the entry in trees will get
+                        * handled first, at which point we'll go into
+                        * "foo", and deal with "bar" from the index,
+                        * because the base will be "foo/". The only
+                        * way we can actually have "foo/bar" first of
+                        * all the things is if the trees don't
+                        * contain "foo" at all, in which case we'll
+                        * handle "foo/bar" without going into the
+                        * directory, but that's fine (and will return
+                        * an error anyway, with the added unknown
+                        * file case.
+                        */
+
+                       cache_name = active_cache[*indpos]->name;
+                       if (strlen(cache_name) > baselen &&
+                           !memcmp(cache_name, base, baselen)) {
+                               cache_name += baselen;
+                               first = cache_name;
+                       } else {
+                               cache_name = NULL;
+                       }
+               }
+
+#if DBRT_DEBUG > 1
+               if (first)
+                       printf("index %s\n", first);
+#endif
+               for (i = 0; i < len; i++) {
+                       if (!posns[i] || posns[i] == df_conflict_list)
+                               continue;
+#if DBRT_DEBUG > 1
+                       printf("%d %s\n", i + 1, posns[i]->name);
+#endif
+                       if (!first || entcmp(first, firstdir,
+                                            posns[i]->name,
+                                            posns[i]->directory) > 0) {
+                               first = posns[i]->name;
+                               firstdir = posns[i]->directory;
+                       }
+               }
+               /* No name means we're done */
+               if (!first)
+                       return 0;
+
+               pathlen = strlen(first);
+               ce_size = cache_entry_size(baselen + pathlen);
+
+               src = xcalloc(src_size, sizeof(struct cache_entry *));
+
+               subposns = xcalloc(len, sizeof(struct tree_list_entry *));
+
+               if (cache_name && !strcmp(cache_name, first)) {
+                       any_files = 1;
+                       src[0] = active_cache[*indpos];
+                       remove_cache_entry_at(*indpos);
+               }
+
+               for (i = 0; i < len; i++) {
+                       struct cache_entry *ce;
+
+                       if (!posns[i] ||
+                           (posns[i] != df_conflict_list &&
+                            strcmp(first, posns[i]->name))) {
+                               continue;
+                       }
+
+                       if (posns[i] == df_conflict_list) {
+                               src[i + o->merge] = o->df_conflict_entry;
+                               continue;
+                       }
+
+                       if (posns[i]->directory) {
+                               struct tree *tree = lookup_tree(posns[i]->sha1);
+                               any_dirs = 1;
+                               parse_tree(tree);
+                               subposns[i] = create_tree_entry_list(tree);
+                               posns[i] = posns[i]->next;
+                               src[i + o->merge] = o->df_conflict_entry;
+                               continue;
+                       }
+
+                       if (!o->merge)
+                               ce_stage = 0;
+                       else if (i + 1 < o->head_idx)
+                               ce_stage = 1;
+                       else if (i + 1 > o->head_idx)
+                               ce_stage = 3;
+                       else
+                               ce_stage = 2;
+
+                       ce = xcalloc(1, ce_size);
+                       ce->ce_mode = create_ce_mode(posns[i]->mode);
+                       ce->ce_flags = create_ce_flags(baselen + pathlen,
+                                                      ce_stage);
+                       memcpy(ce->name, base, baselen);
+                       memcpy(ce->name + baselen, first, pathlen + 1);
+
+                       any_files = 1;
+
+                       memcpy(ce->sha1, posns[i]->sha1, 20);
+                       src[i + o->merge] = ce;
+                       subposns[i] = df_conflict_list;
+                       posns[i] = posns[i]->next;
+               }
+               if (any_files) {
+                       if (o->merge) {
+                               int ret;
+
+#if DBRT_DEBUG > 1
+                               printf("%s:\n", first);
+                               for (i = 0; i < src_size; i++) {
+                                       printf(" %d ", i);
+                                       if (src[i])
+                                               printf("%s\n", sha1_to_hex(src[i]->sha1));
+                                       else
+                                               printf("\n");
+                               }
+#endif
+                               ret = o->fn(src, o);
+
+#if DBRT_DEBUG > 1
+                               printf("Added %d entries\n", ret);
+#endif
+                               *indpos += ret;
+                       } else {
+                               for (i = 0; i < src_size; i++) {
+                                       if (src[i]) {
+                                               add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+                                       }
+                               }
+                       }
+               }
+               if (any_dirs) {
+                       char *newbase = xmalloc(baselen + 2 + pathlen);
+                       memcpy(newbase, base, baselen);
+                       memcpy(newbase + baselen, first, pathlen);
+                       newbase[baselen + pathlen] = '/';
+                       newbase[baselen + pathlen + 1] = '\0';
+                       if (unpack_trees_rec(subposns, len, newbase, o,
+                                            indpos, df_conflict_list))
+                               return -1;
+                       free(newbase);
+               }
+               free(subposns);
+               free(src);
+       } while (1);
+}
+
+/* Unlink the last component and attempt to remove leading
+ * directories, in case this unlink is the removal of the
+ * last entry in the directory -- empty directories are removed.
+ */
+static void unlink_entry(char *name)
+{
+       char *cp, *prev;
+
+       if (unlink(name))
+               return;
+       prev = NULL;
+       while (1) {
+               int status;
+               cp = strrchr(name, '/');
+               if (prev)
+                       *prev = '/';
+               if (!cp)
+                       break;
+
+               *cp = 0;
+               status = rmdir(name);
+               if (status) {
+                       *cp = '/';
+                       break;
+               }
+               prev = cp;
+       }
+}
+
+static volatile int progress_update = 0;
+
+static void progress_interval(int signum)
+{
+       progress_update = 1;
+}
+
+static void setup_progress_signal(void)
+{
+       struct sigaction sa;
+       struct itimerval v;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = progress_interval;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGALRM, &sa, NULL);
+
+       v.it_interval.tv_sec = 1;
+       v.it_interval.tv_usec = 0;
+       v.it_value = v.it_interval;
+       setitimer(ITIMER_REAL, &v, NULL);
+}
+
+static struct checkout state;
+static void check_updates(struct cache_entry **src, int nr,
+               struct unpack_trees_options *o)
+{
+       unsigned short mask = htons(CE_UPDATE);
+       unsigned last_percent = 200, cnt = 0, total = 0;
+
+       if (o->update && o->verbose_update) {
+               for (total = cnt = 0; cnt < nr; cnt++) {
+                       struct cache_entry *ce = src[cnt];
+                       if (!ce->ce_mode || ce->ce_flags & mask)
+                               total++;
+               }
+
+               /* Don't bother doing this for very small updates */
+               if (total < 250)
+                       total = 0;
+
+               if (total) {
+                       fprintf(stderr, "Checking files out...\n");
+                       setup_progress_signal();
+                       progress_update = 1;
+               }
+               cnt = 0;
+       }
+
+       while (nr--) {
+               struct cache_entry *ce = *src++;
+
+               if (total) {
+                       if (!ce->ce_mode || ce->ce_flags & mask) {
+                               unsigned percent;
+                               cnt++;
+                               percent = (cnt * 100) / total;
+                               if (percent != last_percent ||
+                                   progress_update) {
+                                       fprintf(stderr, "%4u%% (%u/%u) done\r",
+                                               percent, cnt, total);
+                                       last_percent = percent;
+                                       progress_update = 0;
+                               }
+                       }
+               }
+               if (!ce->ce_mode) {
+                       if (o->update)
+                               unlink_entry(ce->name);
+                       continue;
+               }
+               if (ce->ce_flags & mask) {
+                       ce->ce_flags &= ~mask;
+                       if (o->update)
+                               checkout_entry(ce, &state, NULL);
+               }
+       }
+       if (total) {
+               signal(SIGALRM, SIG_IGN);
+               fputc('\n', stderr);
+       }
+}
+
+int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
+{
+       int indpos = 0;
+       unsigned len = object_list_length(trees);
+       struct tree_entry_list **posns;
+       int i;
+       struct object_list *posn = trees;
+       struct tree_entry_list df_conflict_list;
+       struct cache_entry df_conflict_entry;
+
+       memset(&df_conflict_list, 0, sizeof(df_conflict_list));
+       df_conflict_list.next = &df_conflict_list;
+       memset(&state, 0, sizeof(state));
+       state.base_dir = "";
+       state.force = 1;
+       state.quiet = 1;
+       state.refresh_cache = 1;
+
+       o->merge_size = len;
+       memset(&df_conflict_entry, 0, sizeof(df_conflict_entry));
+       o->df_conflict_entry = &df_conflict_entry;
+
+       if (len) {
+               posns = xmalloc(len * sizeof(struct tree_entry_list *));
+               for (i = 0; i < len; i++) {
+                       posns[i] = create_tree_entry_list((struct tree *) posn->item);
+                       posn = posn->next;
+               }
+               if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
+                                    o, &indpos, &df_conflict_list))
+                       return -1;
+       }
+
+       if (o->trivial_merges_only && o->nontrivial_merge)
+               die("Merge requires file-level merging");
+
+       check_updates(active_cache, active_nr, o);
+       return 0;
+}
+
+/* Here come the merge functions */
+
+static void reject_merge(struct cache_entry *ce)
+{
+       die("Entry '%s' would be overwritten by merge. Cannot merge.",
+           ce->name);
+}
+
+static int same(struct cache_entry *a, struct cache_entry *b)
+{
+       if (!!a != !!b)
+               return 0;
+       if (!a && !b)
+               return 1;
+       return a->ce_mode == b->ce_mode &&
+               !memcmp(a->sha1, b->sha1, 20);
+}
+
+
+/*
+ * When a CE gets turned into an unmerged entry, we
+ * want it to be up-to-date
+ */
+static void verify_uptodate(struct cache_entry *ce,
+               struct unpack_trees_options *o)
+{
+       struct stat st;
+
+       if (o->index_only || o->reset)
+               return;
+
+       if (!lstat(ce->name, &st)) {
+               unsigned changed = ce_match_stat(ce, &st, 1);
+               if (!changed)
+                       return;
+               errno = 0;
+       }
+       if (o->reset) {
+               ce->ce_flags |= htons(CE_UPDATE);
+               return;
+       }
+       if (errno == ENOENT)
+               return;
+       die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+}
+
+static void invalidate_ce_path(struct cache_entry *ce)
+{
+       if (ce)
+               cache_tree_invalidate_path(active_cache_tree, ce->name);
+}
+
+/*
+ * We do not want to remove or overwrite a working tree file that
+ * is not tracked.
+ */
+static void verify_absent(const char *path, const char *action,
+               struct unpack_trees_options *o)
+{
+       struct stat st;
+
+       if (o->index_only || o->reset || !o->update)
+               return;
+       if (!lstat(path, &st))
+               die("Untracked working tree file '%s' "
+                   "would be %s by merge.", path, action);
+}
+
+static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
+               struct unpack_trees_options *o)
+{
+       merge->ce_flags |= htons(CE_UPDATE);
+       if (old) {
+               /*
+                * See if we can re-use the old CE directly?
+                * That way we get the uptodate stat info.
+                *
+                * This also removes the UPDATE flag on
+                * a match.
+                */
+               if (same(old, merge)) {
+                       *merge = *old;
+               } else {
+                       verify_uptodate(old, o);
+                       invalidate_ce_path(old);
+               }
+       }
+       else {
+               verify_absent(merge->name, "overwritten", o);
+               invalidate_ce_path(merge);
+       }
+
+       merge->ce_flags &= ~htons(CE_STAGEMASK);
+       add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+       return 1;
+}
+
+static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
+               struct unpack_trees_options *o)
+{
+       if (old)
+               verify_uptodate(old, o);
+       else
+               verify_absent(ce->name, "removed", o);
+       ce->ce_mode = 0;
+       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+       invalidate_ce_path(ce);
+       return 1;
+}
+
+static int keep_entry(struct cache_entry *ce)
+{
+       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+       return 1;
+}
+
+#if DBRT_DEBUG
+static void show_stage_entry(FILE *o,
+                            const char *label, const struct cache_entry *ce)
+{
+       if (!ce)
+               fprintf(o, "%s (missing)\n", label);
+       else
+               fprintf(o, "%s%06o %s %d\t%s\n",
+                       label,
+                       ntohl(ce->ce_mode),
+                       sha1_to_hex(ce->sha1),
+                       ce_stage(ce),
+                       ce->name);
+}
+#endif
+
+int threeway_merge(struct cache_entry **stages,
+               struct unpack_trees_options *o)
+{
+       struct cache_entry *index;
+       struct cache_entry *head;
+       struct cache_entry *remote = stages[o->head_idx + 1];
+       int count;
+       int head_match = 0;
+       int remote_match = 0;
+       const char *path = NULL;
+
+       int df_conflict_head = 0;
+       int df_conflict_remote = 0;
+
+       int any_anc_missing = 0;
+       int no_anc_exists = 1;
+       int i;
+
+       for (i = 1; i < o->head_idx; i++) {
+               if (!stages[i])
+                       any_anc_missing = 1;
+               else {
+                       if (!path)
+                               path = stages[i]->name;
+                       no_anc_exists = 0;
+               }
+       }
+
+       index = stages[0];
+       head = stages[o->head_idx];
+
+       if (head == o->df_conflict_entry) {
+               df_conflict_head = 1;
+               head = NULL;
+       }
+
+       if (remote == o->df_conflict_entry) {
+               df_conflict_remote = 1;
+               remote = NULL;
+       }
+
+       if (!path && index)
+               path = index->name;
+       if (!path && head)
+               path = head->name;
+       if (!path && remote)
+               path = remote->name;
+
+       /* First, if there's a #16 situation, note that to prevent #13
+        * and #14.
+        */
+       if (!same(remote, head)) {
+               for (i = 1; i < o->head_idx; i++) {
+                       if (same(stages[i], head)) {
+                               head_match = i;
+                       }
+                       if (same(stages[i], remote)) {
+                               remote_match = i;
+                       }
+               }
+       }
+
+       /* We start with cases where the index is allowed to match
+        * something other than the head: #14(ALT) and #2ALT, where it
+        * is permitted to match the result instead.
+        */
+       /* #14, #14ALT, #2ALT */
+       if (remote && !df_conflict_head && head_match && !remote_match) {
+               if (index && !same(index, remote) && !same(index, head))
+                       reject_merge(index);
+               return merged_entry(remote, index, o);
+       }
+       /*
+        * If we have an entry in the index cache, then we want to
+        * make sure that it matches head.
+        */
+       if (index && !same(index, head)) {
+               reject_merge(index);
+       }
+
+       if (head) {
+               /* #5ALT, #15 */
+               if (same(head, remote))
+                       return merged_entry(head, index, o);
+               /* #13, #3ALT */
+               if (!df_conflict_remote && remote_match && !head_match)
+                       return merged_entry(head, index, o);
+       }
+
+       /* #1 */
+       if (!head && !remote && any_anc_missing)
+               return 0;
+
+       /* Under the new "aggressive" rule, we resolve mostly trivial
+        * cases that we historically had git-merge-one-file resolve.
+        */
+       if (o->aggressive) {
+               int head_deleted = !head && !df_conflict_head;
+               int remote_deleted = !remote && !df_conflict_remote;
+               /*
+                * Deleted in both.
+                * Deleted in one and unchanged in the other.
+                */
+               if ((head_deleted && remote_deleted) ||
+                   (head_deleted && remote && remote_match) ||
+                   (remote_deleted && head && head_match)) {
+                       if (index)
+                               return deleted_entry(index, index, o);
+                       else if (path)
+                               verify_absent(path, "removed", o);
+                       return 0;
+               }
+               /*
+                * Added in both, identically.
+                */
+               if (no_anc_exists && head && remote && same(head, remote))
+                       return merged_entry(head, index, o);
+
+       }
+
+       /* Below are "no merge" cases, which require that the index be
+        * up-to-date to avoid the files getting overwritten with
+        * conflict resolution files.
+        */
+       if (index) {
+               verify_uptodate(index, o);
+       }
+       else if (path)
+               verify_absent(path, "overwritten", o);
+
+       o->nontrivial_merge = 1;
+
+       /* #2, #3, #4, #6, #7, #9, #11. */
+       count = 0;
+       if (!head_match || !remote_match) {
+               for (i = 1; i < o->head_idx; i++) {
+                       if (stages[i]) {
+                               keep_entry(stages[i]);
+                               count++;
+                               break;
+                       }
+               }
+       }
+#if DBRT_DEBUG
+       else {
+               fprintf(stderr, "read-tree: warning #16 detected\n");
+               show_stage_entry(stderr, "head   ", stages[head_match]);
+               show_stage_entry(stderr, "remote ", stages[remote_match]);
+       }
+#endif
+       if (head) { count += keep_entry(head); }
+       if (remote) { count += keep_entry(remote); }
+       return count;
+}
+
+/*
+ * Two-way merge.
+ *
+ * The rule is to "carry forward" what is in the index without losing
+ * information across a "fast forward", favoring a successful merge
+ * over a merge failure when it makes sense.  For details of the
+ * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
+ *
+ */
+int twoway_merge(struct cache_entry **src,
+               struct unpack_trees_options *o)
+{
+       struct cache_entry *current = src[0];
+       struct cache_entry *oldtree = src[1], *newtree = src[2];
+
+       if (o->merge_size != 2)
+               return error("Cannot do a twoway merge of %d trees",
+                            o->merge_size);
+
+       if (current) {
+               if ((!oldtree && !newtree) || /* 4 and 5 */
+                   (!oldtree && newtree &&
+                    same(current, newtree)) || /* 6 and 7 */
+                   (oldtree && newtree &&
+                    same(oldtree, newtree)) || /* 14 and 15 */
+                   (oldtree && newtree &&
+                    !same(oldtree, newtree) && /* 18 and 19*/
+                    same(current, newtree))) {
+                       return keep_entry(current);
+               }
+               else if (oldtree && !newtree && same(current, oldtree)) {
+                       /* 10 or 11 */
+                       return deleted_entry(oldtree, current, o);
+               }
+               else if (oldtree && newtree &&
+                        same(current, oldtree) && !same(current, newtree)) {
+                       /* 20 or 21 */
+                       return merged_entry(newtree, current, o);
+               }
+               else {
+                       /* all other failures */
+                       if (oldtree)
+                               reject_merge(oldtree);
+                       if (current)
+                               reject_merge(current);
+                       if (newtree)
+                               reject_merge(newtree);
+                       return -1;
+               }
+       }
+       else if (newtree)
+               return merged_entry(newtree, current, o);
+       else
+               return deleted_entry(oldtree, current, o);
+}
+
+/*
+ * Bind merge.
+ *
+ * Keep the index entries at stage0, collapse stage1 but make sure
+ * stage0 does not have anything there.
+ */
+int bind_merge(struct cache_entry **src,
+               struct unpack_trees_options *o)
+{
+       struct cache_entry *old = src[0];
+       struct cache_entry *a = src[1];
+
+       if (o->merge_size != 1)
+               return error("Cannot do a bind merge of %d trees\n",
+                            o->merge_size);
+       if (a && old)
+               die("Entry '%s' overlaps.  Cannot bind.", a->name);
+       if (!a)
+               return keep_entry(old);
+       else
+               return merged_entry(a, NULL, o);
+}
+
+/*
+ * One-way merge.
+ *
+ * The rule is:
+ * - take the stat information from stage0, take the data from stage1
+ */
+int oneway_merge(struct cache_entry **src,
+               struct unpack_trees_options *o)
+{
+       struct cache_entry *old = src[0];
+       struct cache_entry *a = src[1];
+
+       if (o->merge_size != 1)
+               return error("Cannot do a oneway merge of %d trees",
+                            o->merge_size);
+
+       if (!a)
+               return deleted_entry(old, old, o);
+       if (old && same(old, a)) {
+               if (o->reset) {
+                       struct stat st;
+                       if (lstat(old->name, &st) ||
+                           ce_match_stat(old, &st, 1))
+                               old->ce_flags |= htons(CE_UPDATE);
+               }
+               return keep_entry(old);
+       }
+       return merged_entry(a, old, o);
+}
diff --git a/unpack-trees.h b/unpack-trees.h
new file mode 100644 (file)
index 0000000..c460162
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef UNPACK_TREES_H
+#define UNPACK_TREES_H
+
+struct unpack_trees_options;
+
+typedef int (*merge_fn_t)(struct cache_entry **src,
+               struct unpack_trees_options *options);
+
+struct unpack_trees_options {
+       int reset;
+       int merge;
+       int update;
+       int index_only;
+       int nontrivial_merge;
+       int trivial_merges_only;
+       int verbose_update;
+       int aggressive;
+       const char *prefix;
+       merge_fn_t fn;
+
+       int head_idx;
+       int merge_size;
+
+       struct cache_entry *df_conflict_entry;
+};
+
+extern int unpack_trees(struct object_list *trees,
+               struct unpack_trees_options *options);
+
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+
+#endif
index f6f5a7e3db6d45f3f174220fff682cd9fbf0ed72..07ecdb4281cfb32cf16dfc7c46c53e6e8d0247bc 100644 (file)
@@ -182,6 +182,8 @@ static void create_pack_file(void)
                ssize_t sz;
                int pe, pu, pollsize;
 
+               reset_timeout();
+
                pollsize = 0;
                pe = pu = -1;