aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2006-07-30 23:42:10 -0700
committerJunio C Hamano <junkio@cox.net>2006-07-30 23:42:10 -0700
commitc1a788aceecb0a8e95d6442938ade9ca43df033e (patch)
tree30d99ae60a57281f75586d64f9853429fa3d26e7
parentf59aac47f3839367d0da04019b0fc2bd61345225 (diff)
parent076b0adcf9dac7bd9d18624087f679cc811aeb77 (diff)
downloadgit-c1a788aceecb0a8e95d6442938ade9ca43df033e.tar.gz
git-c1a788aceecb0a8e95d6442938ade9ca43df033e.tar.xz
Merge branch 'js/read-tree' into js/c-merge-recursive
* 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 ...
-rw-r--r--.gitignore6
-rw-r--r--Documentation/Makefile15
-rw-r--r--Documentation/asciidoc.conf2
-rw-r--r--Documentation/config.txt21
-rw-r--r--Documentation/git-cvsexportcommit.txt8
-rw-r--r--Documentation/git-daemon.txt13
-rw-r--r--Documentation/git-format-patch.txt16
-rw-r--r--Documentation/git-http-fetch.txt8
-rw-r--r--Documentation/git-local-fetch.txt6
-rw-r--r--Documentation/git-tar-tree.txt20
-rw-r--r--Documentation/git.txt12
-rw-r--r--Documentation/urls.txt17
-rw-r--r--INSTALL9
-rw-r--r--Makefile57
-rw-r--r--blame.c2
-rw-r--r--builtin-add.c46
-rw-r--r--builtin-apply.c145
-rw-r--r--builtin-cat-file.c3
-rw-r--r--builtin-check-ref-format.c2
-rw-r--r--builtin-commit-tree.c4
-rw-r--r--builtin-count.c2
-rw-r--r--builtin-diff-files.c4
-rw-r--r--builtin-diff-index.c4
-rw-r--r--builtin-diff-stages.c3
-rw-r--r--builtin-diff-tree.c4
-rw-r--r--builtin-diff.c14
-rw-r--r--builtin-fmt-merge-msg.c4
-rw-r--r--builtin-grep.c3
-rw-r--r--builtin-help.c6
-rw-r--r--builtin-init-db.c2
-rw-r--r--builtin-log.c71
-rw-r--r--builtin-ls-files.c3
-rw-r--r--builtin-ls-tree.c8
-rw-r--r--builtin-mailinfo.c4
-rw-r--r--builtin-mailsplit.c2
-rw-r--r--builtin-mv.c297
-rw-r--r--builtin-prune.c4
-rw-r--r--builtin-push.c2
-rw-r--r--builtin-read-tree.c862
-rw-r--r--builtin-rev-list.c4
-rw-r--r--builtin-rev-parse.c3
-rw-r--r--builtin-rm.c6
-rw-r--r--builtin-show-branch.c14
-rw-r--r--builtin-stripspace.c2
-rw-r--r--builtin-tar-tree.c30
-rw-r--r--builtin-update-index.c3
-rw-r--r--builtin-update-ref.c3
-rw-r--r--builtin-upload-tar.c2
-rw-r--r--builtin-write-tree.c4
-rw-r--r--builtin.h77
-rw-r--r--cache.h2
-rw-r--r--combine-diff.c3
-rw-r--r--config.c5
-rw-r--r--config.mak.in24
-rw-r--r--configure.ac183
-rw-r--r--contrib/emacs/Makefile4
-rw-r--r--contrib/emacs/git.el31
-rw-r--r--daemon.c2
-rw-r--r--diff.c28
-rw-r--r--diff.h11
-rw-r--r--environment.c1
-rw-r--r--fetch.c122
-rw-r--r--fetch.h17
-rwxr-xr-xgit-am.sh12
-rwxr-xr-xgit-clone.sh4
-rwxr-xr-xgit-cvsexportcommit.perl50
-rwxr-xr-xgit-cvsserver.perl89
-rwxr-xr-xgit-fetch.sh19
-rwxr-xr-xgit-instaweb.sh11
-rwxr-xr-xgit-lost-found.sh2
-rwxr-xr-xgit-merge.sh17
-rwxr-xr-xgit-mv.perl250
-rwxr-xr-xgit-pull.sh3
-rwxr-xr-xgit-quiltimport.sh2
-rwxr-xr-xgit-rebase.sh9
-rwxr-xr-xgit-reset.sh5
-rwxr-xr-xgit-resolve.sh7
-rwxr-xr-xgit-svn.perl25
-rw-r--r--git.c236
-rwxr-xr-xgitweb/gitweb.cgi166
-rw-r--r--http-fetch.c45
-rw-r--r--http-push.c2
-rw-r--r--local-fetch.c34
-rw-r--r--log-tree.c10
-rw-r--r--pack-objects.c13
-rw-r--r--read-cache.c39
-rw-r--r--refs.c5
-rw-r--r--revision.c4
-rw-r--r--revision.h4
-rw-r--r--setup.c4
-rw-r--r--sha1_file.c132
-rw-r--r--ssh-fetch.c6
-rwxr-xr-xt/t1400-update-ref.sh12
-rwxr-xr-xt/t4102-apply-rename.sh24
-rwxr-xr-xt/t4103-apply-binary.sh4
-rwxr-xr-xt/t4112-apply-renames.sh28
-rwxr-xr-xt/t4114-apply-typechange.sh105
-rwxr-xr-xt/t6004-rev-list-path-optim.sh19
-rwxr-xr-xt/t7001-mv.sh40
-rw-r--r--unpack-objects.c5
-rw-r--r--unpack-trees.c799
-rw-r--r--unpack-trees.h35
-rw-r--r--upload-pack.c2
103 files changed, 2902 insertions, 1668 deletions
diff --git a/.gitignore b/.gitignore
index 52d61f319..fb0fa3f16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 2b0efe792..0d9ffb4ad 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -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)
#
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 7ce71510d..8196d787a 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -9,6 +9,8 @@
[attributes]
caret=^
+startsb=&#91;
+endsb=&#93;
ifdef::backend-docbook[]
[gitlink-inlinemacro]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0b434c1f1..465eb13e7 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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'
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index 27ac72d98..092d0d673 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -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.
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 4c357daf6..0f7d274ea 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -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
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 4ca0014da..67425dc03 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -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
-------------
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index bc1a13289..3d508094a 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -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>
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
index 87abec1c4..2fbdfe086 100644
--- a/Documentation/git-local-fetch.txt
+++ b/Documentation/git-local-fetch.txt
@@ -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>
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index f2675c419..1e1c7fa85 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -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.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index ce3058182..7310a2b8b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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
---------------------
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 93378d237..26ecba53f 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -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 7da2c8982..ba9778cd4 100644
--- 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
diff --git a/Makefile b/Makefile
index 12ffd45ca..b7a095f0d 100644
--- 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 b04b8f58a..76712b596 100644
--- 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;
diff --git a/builtin-add.c b/builtin-add.c
index 2d2569817..f548b8007 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -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);
diff --git a/builtin-apply.c b/builtin-apply.c
index c903146bb..f8c6763c7 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -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) {
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index 4d36817e5..814fb0743 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -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>");
diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c
index 4a23936af..701de439a 100644
--- a/builtin-check-ref-format.c
+++ b/builtin-check-ref-format.c
@@ -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");
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index ec082bf75..9c9879667 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -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)
diff --git a/builtin-count.c b/builtin-count.c
index 5ee72df24..1d3729aa9 100644
--- a/builtin-count.c
+++ b/builtin-count.c
@@ -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;
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index 81ac2fe64..ac13db70f 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -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);
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index a1fa1b85c..95a3db156 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -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);
diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c
index 9c6270294..5960e0899 100644
--- a/builtin-diff-stages.c
+++ b/builtin-diff-stages.c
@@ -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 */
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index b61066859..24cb2d7f8 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -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);
diff --git a/builtin-diff.c b/builtin-diff.c
index cb38f4456..48d2fd03b 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -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);
}
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index f20b27b8a..c84224ee8 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -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;
diff --git a/builtin-grep.c b/builtin-grep.c
index a79bac305..69b7c4862 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -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;
diff --git a/builtin-help.c b/builtin-help.c
index 335fe5fed..bb0b03f1a 100644
--- a/builtin-help.c
+++ b/builtin-help.c
@@ -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"))
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 7fdd2fa9f..52473edf5 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -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;
diff --git a/builtin-log.c b/builtin-log.c
index 7e5cab15c..82c69d1d0 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -10,11 +10,13 @@
#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);
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 8dae9f70e..79ffe8f42 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -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);
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index b8d0d88ba..261147fdb 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -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] == '-') {
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index ac53f76f6..24a4fc63b 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -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
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index e2a005843..91a699d34 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -138,7 +138,7 @@ out:
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
index 000000000..62ae937cb
--- /dev/null
+++ b/builtin-mv.c
@@ -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;
+}
diff --git a/builtin-prune.c b/builtin-prune.c
index d196c41f1..6a86eb52a 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -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;
diff --git a/builtin-push.c b/builtin-push.c
index 31cbfd738..a82417106 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -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 */
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 6df5d7c5c..8869cedea 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -3,419 +3,17 @@
*
* 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();
}
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 8f3287133..0dee1734a 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -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);
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index b3e4386c1..aca4a3603 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -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);
diff --git a/builtin-rm.c b/builtin-rm.c
index 5deb81171..92d205a71 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -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];
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 260cb221b..2a1b848f6 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -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);
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 2ce1264f7..09cc9108c 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -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;
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index f2e48aae2..7c48db9ec 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -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;
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 1a4200d15..24dca47d8 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -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;
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 83094abe0..5bd71825f 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -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++) {
diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c
index d4fa7b56c..7b401bbb7 100644
--- a/builtin-upload-tar.c
+++ b/builtin-upload-tar.c
@@ -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];
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 449a4d1b5..0289f5993 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -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"))
diff --git a/builtin.h b/builtin.h
index 5339d8627..1c8637ae2 100644
--- 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 8cc0ccbd0..7c69b3063 100644
--- 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;
diff --git a/combine-diff.c b/combine-diff.c
index 1bc148464..919112bba 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -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);
diff --git a/config.c b/config.c
index 8445f7dca..0ac6aebbb 100644
--- 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
index 000000000..04f508ab9
--- /dev/null
+++ b/config.mak.in
@@ -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
index 000000000..c1f7751e6
--- /dev/null
+++ b/configure.ac
@@ -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}"
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index d3619db51..350846de9 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -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)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 34c995046..68de9be0c 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -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)
diff --git a/daemon.c b/daemon.c
index e4ec67630..810837f0c 100644
--- 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 8b4475613..6a7137648 100644
--- 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 a06f95993..2cced530f 100644
--- 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 *);
diff --git a/environment.c b/environment.c
index 97d42b172..42f39d657 100644
--- a/environment.c
+++ b/environment.c
@@ -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 989d7a478..aeb6bf263 100644
--- 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 841bb1af9..be48c6f19 100644
--- 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 */
diff --git a/git-am.sh b/git-am.sh
index db9a20a81..04f011943 100755
--- 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
diff --git a/git-clone.sh b/git-clone.sh
index 036880388..a92b22a13 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -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@@"
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 5d13a5419..99b3dc392 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -16,9 +16,9 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
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 @@ our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX',
TMPDIR => 1,
CLEANUP => 1);
-print Dumper(@ARGV);
# resolve target commit
my $commit;
$commit = pop @ARGV;
@@ -53,12 +52,32 @@ if (@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;
+ }
}
}
@@ -84,12 +103,18 @@ $opt_v && print "Applying to CVS commit $commit from parent $parent\n";
# 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;
@@ -233,6 +258,7 @@ foreach my $f (@dfiles) {
}
print "Commit to CVS\n";
+print "Patch: $title\n";
my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
my $cmd = "cvs commit -F .msg $commitfiles";
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index c30ef7042..2130d5702 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -81,7 +81,7 @@ my $methods = {
# $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;
diff --git a/git-fetch.sh b/git-fetch.sh
index ff1769952..c2eebee79 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -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
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 63b18b99f..16cd351f7 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -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
diff --git a/git-lost-found.sh b/git-lost-found.sh
index ba6d587f3..b928f2ca5 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -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
diff --git a/git-merge.sh b/git-merge.sh
index 9b681159d..d049e1643 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -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
index 75aa8feeb..000000000
--- a/git-mv.perl
+++ /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);
-}
diff --git a/git-pull.sh b/git-pull.sh
index d337bf4da..f38043799 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -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
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index 364baff80..10135da3a 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -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
diff --git a/git-rebase.sh b/git-rebase.sh
index 8c5da7219..cb1a9ce12 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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
diff --git a/git-reset.sh b/git-reset.sh
index 5c0224090..36fc8ce25 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -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
diff --git a/git-resolve.sh b/git-resolve.sh
index 1c7aaefa2..a7bc680d9 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -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
diff --git a/git-svn.perl b/git-svn.perl
index 4530ffe42..6453771f9 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -147,7 +147,7 @@ init_vars();
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 102735af6..7321d6c3f 100644
--- 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:
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 2fd1e5f78..e5fca63b9 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -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 " | " .
diff --git a/http-fetch.c b/http-fetch.c
index 12493fbed..1aad39b4d 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -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"
diff --git a/http-push.c b/http-push.c
index 47686195c..4021e7d92 100644
--- a/http-push.c
+++ b/http-push.c
@@ -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) {
diff --git a/local-fetch.c b/local-fetch.c
index ffa488757..b216bdd55 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -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;
}
diff --git a/log-tree.c b/log-tree.c
index 9d8d46fa0..b67b8dd17 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -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');
}
diff --git a/pack-objects.c b/pack-objects.c
index 04a48b925..861c7f08f 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -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++) {
diff --git a/read-cache.c b/read-cache.c
index 9c0a9fc2a..c375e912a 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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 56db39445..02850b690 100644
--- 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));
diff --git a/revision.c b/revision.c
index 874e349db..a58257ad8 100644
--- a/revision.c
+++ b/revision.c
@@ -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;
diff --git a/revision.h b/revision.h
index c010a0811..0c3b8d990 100644
--- a/revision.h
+++ b/revision.h
@@ -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 4612f110e..358e139d8 100644
--- 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);
}
diff --git a/sha1_file.c b/sha1_file.c
index 8734d501f..43bc2ea0c 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -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;
diff --git a/ssh-fetch.c b/ssh-fetch.c
index 28f7fd917..6e16568f8 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -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;
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 04fab2662..ddc80bbea 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -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" \
@@ -26,6 +28,16 @@ test_expect_success \
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 &&
test $A = $(cat .git/$m)'
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index fbb508d38..22da6a00c 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -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
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 00bd8b15c..ff052699a 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -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
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
index a06f6956d..69e9603c7 100755
--- a/t/t4112-apply-renames.sh
+++ b/t/t4112-apply-renames.sh
@@ -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
index 000000000..ca81d7215
--- /dev/null
+++ b/t/t4114-apply-typechange.sh
@@ -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
index 000000000..5182dbb15
--- /dev/null
+++ b/t/t6004-rev-list-path-optim.sh
@@ -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
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 811a4797a..900ca93cd 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -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
diff --git a/unpack-objects.c b/unpack-objects.c
index 3b824b04a..48c1ee796 100644
--- a/unpack-objects.c
+++ b/unpack-objects.c
@@ -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
index 000000000..a20639be7
--- /dev/null
+++ b/unpack-trees.c
@@ -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
index 000000000..c4601621c
--- /dev/null
+++ b/unpack-trees.h
@@ -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
diff --git a/upload-pack.c b/upload-pack.c
index f6f5a7e3d..07ecdb428 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -182,6 +182,8 @@ static void create_pack_file(void)
ssize_t sz;
int pe, pu, pollsize;
+ reset_timeout();
+
pollsize = 0;
pe = pu = -1;