GIT 0.99.9l aka 1.0rc4
authorJunio C Hamano <junkio@cox.net>
Sun, 4 Dec 2005 07:46:02 +0000 (23:46 -0800)
committerJunio C Hamano <junkio@cox.net>
Sun, 4 Dec 2005 07:46:02 +0000 (23:46 -0800)
90 files changed:
Documentation/fetch-options.txt
Documentation/git-bisect.txt
Documentation/git-cat-file.txt
Documentation/git-diff-files.txt
Documentation/git-mailinfo.txt
Documentation/git-merge.txt
Documentation/git-mv.txt
Documentation/git-read-tree.txt
Documentation/git-svnimport.txt
Documentation/git-tag.txt
Documentation/pull-fetch-param.txt
Documentation/tutorial.txt
Makefile
apply.c
cache.h
cat-file.c
checkout-index.c
clone-pack.c
commit-tree.c
compat/setenv.c [new file with mode: 0644]
config.c
convert-objects.c
daemon.c
debian/changelog
diff-files.c
environment.c
fetch-pack.c
fsck-objects.c
git-add.sh
git-applymbox.sh
git-applypatch.sh
git-bisect.sh
git-branch.sh
git-clone.sh
git-count-objects.sh
git-diff.sh
git-format-patch.sh
git-lost-found.sh
git-ls-remote.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-recursive.py
git-merge.sh
git-mv.perl
git-rebase.sh
git-svnimport.perl
git-tag.sh
git-verify-tag.sh
git.c
gitk
hash-object.c
http-fetch.c
http-push.c
init-db.c
local-fetch.c
ls-files.c
ls-tree.c
mailinfo.c
merge-base.c
merge-index.c
mktag.c
name-rev.c
pack-objects.c
pack-redundant.c
path.c
peek-remote.c
prune-packed.c
quote.c
quote.h
read-tree.c
rev-list.c
send-pack.c
setup.c
shell.c
ssh-fetch.c
ssh-upload.c
t/t0000-basic.sh
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
t/t6020-merge-df.sh [new file with mode: 0755]
t/t6021-merge-criss-cross.sh [new file with mode: 0755]
t/t7001-mv.sh [new file with mode: 0755]
t/test-lib.sh
tar-tree.c
tree.c
tree.h
unpack-file.c
unpack-objects.c
update-server-info.c
write-tree.c
index a25d04a4fa2e6449d4e5ec1beddf305385ae8571..200c9b240dc3f89cd29350332447e48b53bcfdc1 100644 (file)
@@ -4,6 +4,11 @@
        option old data in `.git/FETCH_HEAD` will be overwritten.
 
 -f, \--force::
+       When `git-fetch` is used with `<rbranch>:<lbranch>`
+       refspec, it refuses to update the local branch
+       `<lbranch>` unless the remote branch `<rbranch>` it
+       fetches is a descendant of `<lbranch>`.  This option
+       overrides that check.
 
 -t, \--tags::
        By default, the git core utilities will not fetch and store
index 39fa665d9d6ee7700adc7bb84cb09c517b38cfa0..8a399703dc7d75c8ca3d170e6d18832a7ce8723b 100644 (file)
@@ -8,13 +8,13 @@ git-bisect - Find the change that introduced a bug
 
 SYNOPSIS
 --------
-'git bisect' start
-'git bisect' bad <rev>
-'git bisect' good <rev>
-'git bisect' reset [<branch>]
-'git bisect' visualize
-'git bisect' replay <logfile>
-'git bisect' log
+ 'git bisect' start
+ 'git bisect' bad <rev>
+ 'git bisect' good <rev>
+ 'git bisect' reset [<branch>]
+ 'git bisect' visualize
+ 'git bisect' replay <logfile>
+ 'git bisect' log
 
 DESCRIPTION
 -----------
index ab4dcae21caa190a41c19ec24b4dc2d71a5a73fb..9a7700fa7fae68292dde2e6aa253bcaaebc1793b 100644 (file)
@@ -8,7 +8,7 @@ git-cat-file - Provide content or type information for repository objects
 
 SYNOPSIS
 --------
-'git-cat-file' (-t | -s | <type>) <object>
+'git-cat-file' (-t | -s | -e | <type>) <object>
 
 DESCRIPTION
 -----------
@@ -29,6 +29,10 @@ OPTIONS
        Instead of the content, show the object size identified by
        <object>.
 
+-e::
+       Suppress all output; instead exit with zero status if <object>
+       exists and is a valid object.
+
 <type>::
        Typically this matches the real type of <object> but asking
        for a type that can trivially be dereferenced from the given
@@ -39,8 +43,11 @@ OPTIONS
 
 OUTPUT
 ------
-If '-t' is specified, one of the <type>.  If '-s' is specified,
-the size of the <object> in bytes.
+If '-t' is specified, one of the <type>.
+
+If '-s' is specified, the size of the <object> in bytes.
+
+If '-e' is specified, no output.
 
 Otherwise the raw (though uncompressed) contents of the <object> will
 be returned.
index 3b04bfeec6ad18edffd9b6d44709a10fdda36b8c..67f51265e6aeadf85db582fe89c2c5752b36745a 100644 (file)
@@ -21,6 +21,15 @@ OPTIONS
 -------
 include::diff-options.txt[]
 
+-1 -2 -3 or --base --ours --theirs, and -0::
+       Diff against the "base" version, "our branch" or "their
+       branch" respectively.  With these options, diffs for
+       merged entries are not shown.
++
+The default is to diff against our branch (-2) and the 
+cleanly resolved paths.  The option -0 can be given to
+omit diff output for unmerged entries and just show "Unmerged".
+
 -q::
        Remain silent even on nonexisting files
 
index dc7d725ea155f9eccb7ebab7df6335ae7589d483..8890754740e796bf5ffb815cb8ff0ec1d4b62d97 100644 (file)
@@ -8,7 +8,7 @@ git-mailinfo - Extracts patch from a single e-mail message.
 
 SYNOPSIS
 --------
-'git-mailinfo' [-k] [-u] <msg> <patch>
+'git-mailinfo' [-k] [-u | --encoding=<encoding>] <msg> <patch>
 
 
 DESCRIPTION
@@ -37,10 +37,17 @@ OPTIONS
        author email are taken from the e-mail without any
        charset conversion, after minimally decoding MIME
        transfer encoding.  This flag causes the resulting
-       commit to be encoded in utf-8 by transliterating them.
+       commit to be encoded in the encoding specified by
+       i18n.commitencoding configuration (defaults to utf-8) by
+       transliterating them. 
        Note that the patch is always used as is without charset
        conversion, even with this flag.
 
+--encoding=<encoding>::
+       Similar to -u but if the local convention is different
+       from what is specified by i18n.commitencoding, this flag
+       can be used to override it.
+
 <msg>::
        The commit log message extracted from e-mail, usually
        except the title line which comes from e-mail Subject.
index 904e2fc4c5d1d2748e8f34b8df977a88f23233fd..0cac563d40f17361569923aebb51e454fd24f92e 100644 (file)
@@ -37,6 +37,103 @@ include::merge-options.txt[]
 include::merge-strategies.txt[]
 
 
+HOW MERGE WORKS
+---------------
+
+A merge is always between the current `HEAD` and one or more
+remote branch heads, and the index file must exactly match the
+tree of `HEAD` commit (i.e. the contents of the last commit) when
+it happens.  In other words, `git-diff --cached HEAD` must
+report no changes.
+
+[NOTE]
+This is a bit of lie.  In certain special cases, your index are
+allowed to be different from the tree of `HEAD` commit.  The most
+notable case is when your `HEAD` commit is already ahead of what
+is being merged, in which case your index can have arbitrary
+difference from your `HEAD` commit.  Otherwise, your index entries
+are allowed have differences from your `HEAD` commit that match
+the result of trivial merge (e.g. you received the same patch
+from external source to produce the same result as what you are
+merging).  For example, if a path did not exist in the common
+ancestor and your head commit but exists in the tree you are
+merging into your repository, and if you already happen to have
+that path exactly in your index, the merge does not have to
+fail.
+
+Otherwise, merge will refuse to do any harm to your repository
+(that is, it may fetch the objects from remote, and it may even
+update the local branch used to keep track of the remote branch
+with `git pull remote rbranch:lbranch`, but your working tree,
+`.git/HEAD` pointer and index file are left intact).
+
+You may have local modifications in the working tree files.  In
+other words, `git-diff` is allowed to report changes.
+However, the merge uses your working tree as the working area,
+and in order to prevent the merge operation from losing such
+changes, it makes sure that they do not interfere with the
+merge. Those complex tables in read-tree documentation define
+what it means for a path to "interfere with the merge".  And if
+your local modifications interfere with the merge, again, it
+stops before touching anything.
+
+So in the above two "failed merge" case, you do not have to
+worry about lossage of data --- you simply were not ready to do
+a merge, so no merge happened at all.  You may want to finish
+whatever you were in the middle of doing, and retry the same
+pull after you are done and ready.
+
+When things cleanly merge, these things happen:
+
+1. the results are updated both in the index file and in your
+   working tree,
+2. index file is written out as a tree,
+3. the tree gets committed, and 
+4. the `HEAD` pointer gets advanced.
+
+Because of 2., we require that the original state of the index
+file to match exactly the current `HEAD` commit; otherwise we
+will write out your local changes already registered in your
+index file along with the merge result, which is not good.
+Because 1. involves only the paths different between your
+branch and the remote branch you are pulling from during the
+merge (which is typically a fraction of the whole tree), you can
+have local modifications in your working tree as long as they do
+not overlap with what the merge updates.
+
+When there are conflicts, these things happen:
+
+1. `HEAD` stays the same.
+
+2. Cleanly merged paths are updated both in the index file and
+   in your working tree.
+
+3. For conflicting paths, the index file records up to three
+   versions; stage1 stores the version from the common ancestor,
+   stage2 from `HEAD`, and stage3 from the remote branch (you
+   can inspect the stages with `git-ls-files -u`).  The working
+   tree files have the result of "merge" program; i.e. 3-way
+   merge result with familiar conflict markers `<<< === >>>`.
+
+4. No other changes are done.  In particular, the local
+   modifications you had before you started merge will stay the
+   same and the index entries for them stay as they were,
+   i.e. matching `HEAD`.
+
+After seeing a conflict, you can do two things:
+
+ * Decide not to merge.  The only clean-up you need are to reset
+   the index file to the `HEAD` commit to reverse 2. and to clean
+   up working tree changes made by 2. and 3.; `git-reset` can
+   be used for this.
+
+ * Resolve the conflicts.  `git-diff` would report only the
+   conflicting paths because of the above 2. and 3..  Edit the
+   working tree files into a desirable shape, `git-update-index`
+   them, to make the index file contain what the merge result
+   should be, and run `git-commit` to commit the result.
+
+
 SEE ALSO
 --------
 gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1]
index f2d5882f48759fc463596e5d970e2f19785c5ebc..3013b8d0c21b4895f1eea05abfb8c89cd766bfb4 100644 (file)
@@ -8,14 +8,14 @@ git-mv - Script used to move or rename a file, directory or symlink.
 
 SYNOPSIS
 --------
-'git-mv' [-f] [-n] <source> <destination>
-'git-mv' [-f] [-k] [-n] <source> ... <destination directory>
+ 'git-mv' [-f] [-n] <source> <destination>
+ 'git-mv' [-f] [-n] [-k] <source> ... <destination directory>
 
 DESCRIPTION
 -----------
 This script is used to move or rename a file, directory or symlink.
 In the first form, it renames <source>, which must exist and be either
-a file, symlink or directory, to <destination>, which must not exist.
+a file, symlink or directory, to <destination>.
 In the second form, the last argument has to be an existing
 directory; the given sources will be moved into this directory.
 
@@ -25,7 +25,7 @@ committed.
 OPTIONS
 -------
 -f::
-       Force renaming or moving even targets exist
+       Force renaming or moving of a file even if the target exists
 -k::
         Skip move or rename actions which would lead to an error
        condition. An error happens when a source is neither existing nor
index 8b91847856b51fbe6727ad96e6d5e4d1fd00c815..6e92e4aa66674fa5fde90866c0f2ff9155270dc0 100644 (file)
@@ -28,11 +28,14 @@ will be in unmerged state when "git-read-tree" returns.
 OPTIONS
 -------
 -m::
-       Perform a merge, not just a read.
+       Perform a merge, not just a read.  The command will
+       refuse to run if your index file has unmerged entries,
+       indicating that you have not finished previous merge you
+       started.
 
 --reset::
-
-        Same as -m except that unmerged entries will be silently ignored.
+        Same as -m, except that unmerged entries are discarded
+        instead of failing.
 
 -u::
        After a successful merge, update the files in the work
@@ -47,7 +50,6 @@ OPTIONS
        trees that are not directly related to the current
        working tree status into a temporary index file.
 
-
 <tree-ish#>::
        The id of the tree object(s) to be read/merged.
 
index fcc79fa93a18444e5df42bc9e98bb18fe4b55ce5..f8dbee70961f932e1c2be29bd11d862d9b1c002c 100644 (file)
@@ -10,7 +10,7 @@ git-svnimport - Import a SVN repository into git
 SYNOPSIS
 --------
 'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
-                       [ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_nr_changes]
+                       [ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
                        [ -b branch_subdir ] [ -t trunk_subdir ] [ -T tag_subdir ]
                        [ -s start_chg ] [ -m ] [ -M regex ]
                        <SVN_repository_URL> [ <path> ]
@@ -71,14 +71,11 @@ When importing incementally, you might need to edit the .git/svn2git file.
        regex. It can be used with -m to also see the default regexes.
        You must escape forward slashes.
 
--l <max_num_changes>::
-       Limit the number of SVN changesets we pull before quitting.
-       This option is necessary because the SVN library has serious memory
-       leaks; the recommended value for nontrivial imports is 100.
+-l <max_rev>::
+       Specify a maximum revision number to pull.
 
-       git-svnimport will still exit with a zero exit code. You can check
-       the size of the file ".git/svn2git" to determine whether to call
-       the importer again.
+       Formerly, this option controlled how many revisions to pull, due to
+       SVN memory leaks. (These have been worked around.)
 
 -v::
        Verbosity: let 'svnimport' report what it is doing.
index 95de436c10d3b43b5c5eac719938005b11ead739..841c9dcf97e3464753818b7e2b49b5e6594c2364 100644 (file)
@@ -10,6 +10,26 @@ SYNOPSIS
 --------
 'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
 
+OPTIONS
+-------
+-a::
+       Make an unsigned, annotated tag object
+
+-s::
+       Make a GPG-signed tag, using the default e-mail address's key
+
+-u <key-id>::
+       Make a GPG-signed tag, using the given key
+
+-f::
+       Replace an existing tag with the given name (instead of failing)
+
+-d::
+       Delete an existing tag with the given name
+
+-m <msg>::
+       Use the given tag message (instead of prompting)
+
 DESCRIPTION
 -----------
 Adds a 'tag' reference in .git/refs/tags/
@@ -23,7 +43,7 @@ creates a 'tag' object, and requires the tag message.  Unless
 in the tag message.
 
 Otherwise just the SHA1 object name of the commit object is
-written (i.e. an lightweight tag).
+written (i.e. a lightweight tag).
 
 A GnuPG signed tag object will be created when `-s` or `-u
 <key-id>` is used.  When `-u <key-id>` is not used, the
index 6413d525ce1d7fa046f81b1d31c6e8ca3b2dbecb..b5b979242ca809d121ef10826ab083159ea2195c 100644 (file)
 - ssh://host.xz/~/path/to/repo.git
 ===============================================================
 +
-       SSH Is the default transport protocol and also supports an
-       scp-like syntax.  Both syntaxes support username expansion,
-       as does the native git protocol. The following three are
-       identical to the last three above, respectively:
+SSH Is the default transport protocol and also supports an
+scp-like syntax.  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/
@@ -26,8 +26,8 @@
 - host.xz:path/to/repo.git
 ===============================================================
 +
-       To sync with a local directory, use:
-
+To sync with a local directory, use:
++
 ===============================================================
 - /path/to/repo.git/
 ===============================================================
@@ -113,7 +113,7 @@ on the remote branch, merge it into your development branch with
 `git pull . remote-B`, while you are on `my-B` branch.
 The common `Pull: master:origin` mapping of a remote `master`
 branch to a local `origin` branch, which is then merged to a
-ocal development branch, again typically named `master`, is made
+local development branch, again typically named `master`, is made
 when you run `git clone` for you to follow this pattern.
 +
 [NOTE]
index e2dfb00ab10de601204715e4b244420a0a2cb7c8..cf7ba76ddfb76b4e92b71474a3386d0573820ac5 100644 (file)
@@ -898,9 +898,8 @@ file, which had no differences in the `mybranch` branch), and say:
        fatal: Merge requires file-level merging
        Nope.
        ...
-       merge: warning: conflicts during merge
-       ERROR: Merge conflict in hello.
-       fatal: merge program failed
+       Auto-merging hello 
+       CONFLICT (content): Merge conflict in hello 
        Automatic merge failed/prevented; fix up by hand
 ----------------
 
@@ -942,10 +941,10 @@ environment, is `git show-branch`.
 
 ------------------------------------------------
 $ git show-branch master mybranch
-* [master] Merged "mybranch" changes.
+* [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
-+  [master] Merged "mybranch" changes.
++  [master] Merge work in mybranch
 ++ [mybranch] Some work.
 ------------------------------------------------
 
@@ -998,10 +997,10 @@ looks like, or run `show-branch`, which tells you this.
 
 ------------------------------------------------
 $ git show-branch master mybranch
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
 --
-++ [master] Merged "mybranch" changes.
+++ [master] Merge work in mybranch
 ------------------------------------------------
 
 
index adff025a8a4d0c88d16c67e3e5995fba52df1109..ba05986c4031760cbdd9ce880316b66dfa134a7d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all:
+
 # Define MOZILLA_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
 # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
@@ -18,6 +21,8 @@
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
+# Define NO_SETENV if you don't have setenv in the C library.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -50,7 +55,7 @@
 # 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.
 
-GIT_VERSION = 0.99.9k
+GIT_VERSION = 0.99.9l
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
@@ -138,8 +143,6 @@ ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
 
-GIT_LIST_TWEAK =
-
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@ -154,20 +157,6 @@ endif
 PYMODULES = \
        gitMergeCommon.py
 
-ifdef WITH_OWN_SUBPROCESS_PY
-       PYMODULES += compat/subprocess.py
-else
-       ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
-               PYMODULES += compat/subprocess.py
-       endif
-endif
-
-ifdef WITH_SEND_EMAIL
-       SCRIPT_PERL += git-send-email.perl
-else
-       GIT_LIST_TWEAK += -e '/^send-email$$/d'
-endif
-
 LIB_FILE=libgit.a
 
 LIB_H = \
@@ -207,6 +196,7 @@ shellquote = '$(call shq,$(1))'
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
 uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
 uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
 
 ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
@@ -224,6 +214,9 @@ ifeq ($(uname_S),SunOS)
        NEEDS_LIBICONV = YesPlease
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
+       ifeq ($(uname_R),5.8)
+               NO_SETENV = YesPlease
+       endif
        INSTALL = ginstall
        TAR = gtar
        ALL_CFLAGS += -D__EXTENSIONS__
@@ -256,6 +249,18 @@ endif
 
 -include config.mak
 
+ifdef WITH_OWN_SUBPROCESS_PY
+       PYMODULES += compat/subprocess.py
+else
+       ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
+               PYMODULES += compat/subprocess.py
+       endif
+endif
+
+ifdef WITH_SEND_EMAIL
+       SCRIPT_PERL += git-send-email.perl
+endif
+
 ifndef NO_CURL
        ifdef CURLDIR
                # This is still problematic -- gcc does not always want -R.
@@ -315,12 +320,16 @@ ifdef NEEDS_NSL
        SIMPLE_LIB += -lnsl
 endif
 ifdef NO_STRCASESTR
-       ALL_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
-       LIB_OBJS += compat/strcasestr.o
+       COMPAT_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
+       COMPAT_OBJS += compat/strcasestr.o
+endif
+ifdef NO_SETENV
+       COMPAT_CFLAGS += -Dsetenv=gitsetenv -DNO_SETENV=1
+       COMPAT_OBJS += compat/setenv.o
 endif
 ifdef NO_MMAP
-       ALL_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
-       LIB_OBJS += compat/mmap.o
+       COMPAT_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
+       COMPAT_OBJS += compat/mmap.o
 endif
 ifdef NO_IPV6
        ALL_CFLAGS += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
@@ -344,8 +353,8 @@ endif
 endif
 endif
 
-ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
-
+ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) $(COMPAT_CFLAGS)
+LIB_OBJS += $(COMPAT_OBJS)
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
@@ -354,10 +363,9 @@ all: $(ALL_PROGRAMS)
 all:
        $(MAKE) -C templates
 
-# Only use $(CFLAGS). We don't need anything else.
-git$(X): git.c Makefile
+git$(X): git.c $(COMPAT_OBJS) Makefile
        $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
-               $(CFLAGS) $< -o $@
+               $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(filter %.o,$^)
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        rm -f $@
diff --git a/apply.c b/apply.c
index 50be8f3e22d0906d09410b89ebc8d06f928ba631..1742ab28e95936eed52e910930a8a04869c5418b 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -16,6 +16,9 @@
 //  --numstat does numeric diffstat, and doesn't actually apply
 //  --index-info shows the old and new index info for paths if available.
 //
+static const char *prefix;
+static int prefix_length = -1;
+
 static int allow_binary_replacement = 0;
 static int check_index = 0;
 static int write_index = 0;
@@ -1706,6 +1709,12 @@ static int use_patch(struct patch *p)
                        return 0;
                x = x->next;
        }
+       if (0 < prefix_length) {
+               int pathlen = strlen(pathname);
+               if (pathlen <= prefix_length ||
+                   memcmp(prefix, pathname, prefix_length))
+                       return 0;
+       }
        return 1;
 }
 
@@ -1845,6 +1854,15 @@ int main(int argc, char **argv)
                        line_termination = 0;
                        continue;
                }
+
+               if (check_index && prefix_length < 0) {
+                       prefix = setup_git_directory();
+                       prefix_length = prefix ? strlen(prefix) : 0;
+                       git_config(git_default_config);
+               }
+               if (0 < prefix_length)
+                       arg = prefix_filename(prefix, prefix_length, arg);
+
                fd = open(arg, O_RDONLY);
                if (fd < 0)
                        usage(apply_usage);
diff --git a/cache.h b/cache.h
index 6ac94c5a1f0ccdc956b006eaa9a176835f5726fb..f9b367f3142b9841a737b0738bb2a422922885ce 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -147,8 +147,10 @@ extern char *get_graft_file(void);
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
+extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern const char *prefix_path(const char *prefix, int len, const char *path);
+extern const char *prefix_filename(const char *prefix, int len, const char *path);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
@@ -182,6 +184,10 @@ extern int trust_executable_bit;
 extern int only_use_symrefs;
 extern int diff_rename_limit_default;
 
+#define GIT_REPO_VERSION 0
+extern int repository_format_version;
+extern int check_repository_format(void);
+
 #define MTIME_CHANGED  0x0001
 #define CTIME_CHANGED  0x0002
 #define OWNER_CHANGED  0x0004
@@ -383,16 +389,21 @@ extern int gitfakemunmap(void *start, size_t length);
 
 typedef int (*config_fn_t)(const char *, const char *);
 extern int git_default_config(const char *, const char *);
+extern int git_config_from_file(config_fn_t fn, const char *);
 extern int git_config(config_fn_t fn);
 extern int git_config_int(const char *, const char *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
 extern char git_default_email[MAX_GITNAME];
 extern char git_default_name[MAX_GITNAME];
 
+#define MAX_ENCODING_LENGTH 64
+extern char git_commit_encoding[MAX_ENCODING_LENGTH];
+
 /* Sane ctype - no locale, and works with signed chars */
 #undef isspace
 #undef isdigit
index d775a1545beb84caf05cae8980fb2d839240fdea..7594108c6e5f7d8830c7b5ca6f45951b8b4bcc23 100644 (file)
@@ -11,27 +11,44 @@ int main(int argc, char **argv)
        char type[20];
        void *buf;
        unsigned long size;
+       int opt;
 
        setup_git_directory();
        if (argc != 3 || get_sha1(argv[2], sha1))
-               usage("git-cat-file [-t | -s | <type>] <sha1>");
-
-       if (!strcmp("-t", argv[1]) || !strcmp("-s", argv[1])) {
-               if (!sha1_object_info(sha1, type,
-                                     argv[1][1] == 's' ? &size : NULL)) {
-                       switch (argv[1][1]) {
-                       case 't':
-                               printf("%s\n", type);
-                               break;
-                       case 's':
-                               printf("%lu\n", size);
-                               break;
-                       }
+               usage("git-cat-file [-t|-s|-e|<type>] <sha1>");
+
+       opt = 0;
+       if ( argv[1][0] == '-' ) {
+               opt = argv[1][1];
+               if ( !opt || argv[1][2] )
+                       opt = -1; /* Not a single character option */
+       }
+
+       buf = NULL;
+       switch (opt) {
+       case 't':
+               if (!sha1_object_info(sha1, type, NULL)) {
+                       printf("%s\n", type);
                        return 0;
                }
-               buf = NULL;
-       } else {
+               break;
+
+       case 's':
+               if (!sha1_object_info(sha1, type, &size)) {
+                       printf("%lu\n", size);
+                       return 0;
+               }
+               break;
+
+       case 'e':
+               return !has_sha1_file(sha1);
+
+       case 0:
                buf = read_object_with_reference(sha1, argv[1], &size, NULL);
+               break;
+
+       default:
+               die("git-cat-file: unknown option: %s\n", argv[1]);
        }
 
        if (!buf)
index dab3778a9585bcfde98be436255542df13aacddb..f1e716d4126c7dc9a0fd1be665dee9f8d0fbc87e 100644 (file)
@@ -34,6 +34,9 @@
  */
 #include "cache.h"
 
+static const char *prefix;
+static int prefix_length;
+
 static struct checkout state = {
        .base_dir = "",
        .base_dir_len = 0,
@@ -69,6 +72,10 @@ static int checkout_all(void)
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce))
                        continue;
+               if (prefix && *prefix &&
+                   ( ce_namelen(ce) <= prefix_length ||
+                     memcmp(prefix, ce->name, prefix_length) ))
+                       continue;
                if (checkout_entry(ce, &state) < 0)
                        errs++;
        }
@@ -91,6 +98,9 @@ int main(int argc, char **argv)
        int newfd = -1;
        int all = 0;
 
+       prefix = setup_git_directory();
+       prefix_length = prefix ? strlen(prefix) : 0;
+
        if (read_cache() < 0) {
                die("invalid cache");
        }
@@ -155,7 +165,7 @@ int main(int argc, char **argv)
 
                if (all)
                        die("git-checkout-index: don't mix '--all' and explicit filenames");
-               checkout_file(arg);
+               checkout_file(prefix_path(prefix, prefix_length, arg));
        }
 
        if (all)
index 960921903eaa712523af0b03098970127729f363..a99a95c5f26718bd509a1c8906663fd73447f16d 100644 (file)
@@ -271,6 +271,8 @@ int main(int argc, char **argv)
        int fd[2];
        pid_t pid;
 
+       setup_git_directory();
+
        nr_heads = 0;
        heads = NULL;
        for (i = 1; i < argc; i++) {
index b60299fed0442edec21f123e92774238d4533bea..4634b50e6aa8bb214da7ebf41d2a8cf34028f531 100644 (file)
@@ -91,6 +91,8 @@ int main(int argc, char **argv)
        if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
                usage(commit_tree_usage);
 
+       setup_git_directory();
+
        check_valid(tree_sha1, "tree");
        for (i = 2; i < argc; i += 2) {
                char *a, *b;
diff --git a/compat/setenv.c b/compat/setenv.c
new file mode 100644 (file)
index 0000000..94acd2d
--- /dev/null
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <string.h>
+
+int gitsetenv(const char *name, const char *value, int replace)
+{
+       int out;
+       size_t namelen, valuelen;
+       char *envstr;
+
+       if (!name || !value) return -1;
+       if (!replace) {
+               char *oldval = NULL;
+               oldval = getenv(name);
+               if (oldval) return 0;
+       }
+
+       namelen = strlen(name);
+       valuelen = strlen(value);
+       envstr = malloc((namelen + valuelen + 2) * sizeof(char));
+       if (!envstr) return -1;
+
+       memcpy(envstr, name, namelen);
+       envstr[namelen] = '=';
+       memcpy(envstr + namelen + 1, value, valuelen);
+       envstr[namelen + valuelen + 1] = 0;
+
+       out = putenv(envstr);
+
+       free(envstr);
+       return out;
+}
index 5cc853508ac3ccd29a7142a032eae9df196d79ea..34584f62b40ef256d1d0f75e2ee16ed30b1c11fa 100644 (file)
--- a/config.c
+++ b/config.c
@@ -11,6 +11,7 @@
 #define MAXNAME (256)
 
 static FILE *config_file;
+static const char *config_file_name;
 static int config_linenr;
 static int get_next_char(void)
 {
@@ -186,7 +187,7 @@ static int git_parse_file(config_fn_t fn)
                if (get_value(fn, var, baselen+1) < 0)
                        break;
        }
-       die("bad config file line %d", config_linenr);
+       die("bad config file line %d in %s", config_linenr, config_file_name);
 }
 
 int git_config_int(const char *name, const char *value)
@@ -197,7 +198,7 @@ int git_config_int(const char *name, const char *value)
                if (!*end)
                        return val;
        }
-       die("bad config value for '%s'", name);
+       die("bad config value for '%s' in %s", name, config_file_name);
 }
 
 int git_config_bool(const char *name, const char *value)
@@ -236,25 +237,37 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "i18n.commitencoding")) {
+               strncpy(git_commit_encoding, value, sizeof(git_commit_encoding));
+               return 0;
+       }
+
        /* Add other config variables here.. */
        return 0;
 }
 
-int git_config(config_fn_t fn)
+int git_config_from_file(config_fn_t fn, const char *filename)
 {
        int ret;
-       FILE *f = fopen(git_path("config"), "r");
+       FILE *f = fopen(filename, "r");
 
        ret = -1;
        if (f) {
                config_file = f;
+               config_file_name = filename;
                config_linenr = 1;
                ret = git_parse_file(fn);
                fclose(f);
+               config_file_name = NULL;
        }
        return ret;
 }
 
+int git_config(config_fn_t fn)
+{
+       return git_config_from_file(fn, git_path("config"));
+}
+
 /*
  * Find all the stuff for git_config_set() below.
  */
index a892013f0f37480fbf13a9511f2146d1681ba136..d78a8b4ae3af80a1207b0e8894ca55fe2363114b 100644 (file)
@@ -316,6 +316,8 @@ int main(int argc, char **argv)
        unsigned char sha1[20];
        struct entry *entry;
 
+       setup_git_directory();
+
        if (argc != 2 || get_sha1(argv[1], sha1))
                usage("git-convert-objects <sha1>");
 
index 91b96569cd75c77cd4ee169c55be045daccf3908..539f6e87af9824ea331326ade71e343698e235fe 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -82,9 +82,63 @@ static void loginfo(const char *err, ...)
        va_end(params);
 }
 
+static int avoid_alias(char *p)
+{
+       int sl, ndot;
+
+       /* 
+        * This resurrects the belts and suspenders paranoia check by HPA
+        * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+        * does not do getcwd() based path canonicalizations.
+        *
+        * sl becomes true immediately after seeing '/' and continues to
+        * be true as long as dots continue after that without intervening
+        * non-dot character.
+        */
+       if (!p || (*p != '/' && *p != '~'))
+               return -1;
+       sl = 1; ndot = 0;
+       p++;
+
+       while (1) {
+               char ch = *p++;
+               if (sl) {
+                       if (ch == '.')
+                               ndot++;
+                       else if (ch == '/') {
+                               if (ndot < 3)
+                                       /* reject //, /./ and /../ */
+                                       return -1;
+                               ndot = 0;
+                       }
+                       else if (ch == 0) {
+                               if (0 < ndot && ndot < 3)
+                                       /* reject /.$ and /..$ */
+                                       return -1;
+                               return 0;
+                       }
+                       else
+                               sl = ndot = 0;
+               }
+               else if (ch == 0)
+                       return 0;
+               else if (ch == '/') {
+                       sl = 1;
+                       ndot = 0;
+               }
+       }
+}
+
 static char *path_ok(char *dir)
 {
-       char *path = enter_repo(dir, strict_paths);
+       char *path;
+
+       if (avoid_alias(dir)) {
+               logerror("'%s': aliased", dir);
+               return NULL;
+       }
+
+       path = enter_repo(dir, strict_paths);
 
        if (!path) {
                logerror("'%s': unable to chdir or not a git archive", dir);
@@ -96,9 +150,11 @@ static char *path_ok(char *dir)
                int pathlen = strlen(path);
 
                /* The validation is done on the paths after enter_repo
-                * canonicalization, so whitelist should be written in
-                * terms of real pathnames (i.e. after ~user is expanded
-                * and symlinks resolved).
+                * appends optional {.git,.git/.git} and friends, but 
+                * it does not use getcwd().  So if your /pub is
+                * a symlink to /mnt/pub, you can whitelist /pub and
+                * do not have to say /mnt/pub.
+                * Do not say /pub/.
                 */
                for ( pp = ok_paths ; *pp ; pp++ ) {
                        int len = strlen(*pp);
index 7356fe778046c0b6fb1bb96e11f5b75a12ceb82b..7e5d1ac8809d90c89cbffd67c892c3121f2dc184 100644 (file)
@@ -1,3 +1,9 @@
+git-core (0.99.9l-0) unstable; urgency=low
+
+  * GIT 0.99.9l aka 1.0rc4
+
+ -- Junio C Hamano <junkio@cox.net>  Sat,  3 Dec 2005 23:45:23 -0800
+
 git-core (0.99.9k-0) unstable; urgency=low
 
   * GIT 0.99.9k but not 1.0rc yet.
index 38599b5b755df431e3e061df8a304698cc4496fc..6c0696c34f0af95acbfe06f644f7df7fb1e93509 100644 (file)
@@ -7,12 +7,12 @@
 #include "diff.h"
 
 static const char diff_files_usage[] =
-"git-diff-files [-q] "
-"[<common diff options>] [<path>...]"
+"git-diff-files [-q] [-0/-1/2/3] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
 static struct diff_options diff_options;
 static int silent = 0;
+static int diff_unmerged_stage = 2;
 
 static void show_unmerge(const char *path)
 {
@@ -46,7 +46,21 @@ int main(int argc, const char **argv)
                        argc--;
                        break;
                }
-               if (!strcmp(argv[1], "-q"))
+               if (!strcmp(argv[1], "-0"))
+                       diff_unmerged_stage = 0;
+               else if (!strcmp(argv[1], "-1"))
+                       diff_unmerged_stage = 1;
+               else if (!strcmp(argv[1], "-2"))
+                       diff_unmerged_stage = 2;
+               else if (!strcmp(argv[1], "-3"))
+                       diff_unmerged_stage = 3;
+               else if (!strcmp(argv[1], "--base"))
+                       diff_unmerged_stage = 1;
+               else if (!strcmp(argv[1], "--ours"))
+                       diff_unmerged_stage = 2;
+               else if (!strcmp(argv[1], "--theirs"))
+                       diff_unmerged_stage = 3;
+               else if (!strcmp(argv[1], "-q"))
                        silent = 1;
                else if (!strcmp(argv[1], "-r"))
                        ; /* no-op */
@@ -95,11 +109,26 @@ int main(int argc, const char **argv)
 
                if (ce_stage(ce)) {
                        show_unmerge(ce->name);
-                       while (i < entries &&
-                              !strcmp(ce->name, active_cache[i]->name))
+                       while (i < entries) {
+                               struct cache_entry *nce = active_cache[i];
+
+                               if (strcmp(ce->name, nce->name))
+                                       break;
+                               /* diff against the proper unmerged stage */
+                               if (ce_stage(nce) == diff_unmerged_stage)
+                                       ce = nce;
                                i++;
-                       i--; /* compensate for loop control increments */
-                       continue;
+                       }
+                       /*
+                        * Compensate for loop update
+                        */
+                       i--;
+                       /*
+                        * Show the diff for the 'ce' if we found the one
+                        * from the desired stage.
+                        */
+                       if (ce_stage(ce) != diff_unmerged_stage)
+                               continue;
                }
 
                if (lstat(ce->name, &st) < 0) {
index b5026f1265b771bc3f9906507b015fbd1d68f8fb..0886ad38f94ddb897629d832160333921ac89481 100644 (file)
@@ -13,6 +13,8 @@ char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int only_use_symrefs = 0;
+int repository_format_version = 0;
+char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 
 static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
        *git_graft_file;
index 65659826601eb9c850266539f6c9e7807a0b467e..58ba2094dc6eda7e0429cf83730f8c4c84e4c084 100644 (file)
@@ -424,6 +424,8 @@ int main(int argc, char **argv)
        int fd[2];
        pid_t pid;
 
+       setup_git_directory();
+
        nr_heads = 0;
        heads = NULL;
        for (i = 1; i < argc; i++) {
index 0433a1d0da6d6286c3db5a2395e2b2e60c9e9472..90e638e573be005033cbf7faeff6ea8e97a1e758 100644 (file)
@@ -431,6 +431,8 @@ int main(int argc, char **argv)
 {
        int i, heads;
 
+       setup_git_directory();
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
index b5fe46aa20865d6785390fd22406d32b00a77845..fdec86d1a41af847f9d737a2c058e2d2027f32fe 100755 (executable)
@@ -1,5 +1,10 @@
 #!/bin/sh
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 usage() {
     die "usage: git add [-n] [-v] <file>..."
 }
index 24d4a8cb4eb95a5201c205be879d3ac6a62a8a15..c686cc8d27a64998c141cca2989375910601f90f 100755 (executable)
@@ -33,7 +33,7 @@ do
        -k)     keep_subject=-k ;;
        -q)     query_apply=t ;;
        -c)     continue="$2"; resume=f; shift ;;
-       -m)     fallback_3way=t ;;
+       -m)     fall_back_3way=t ;;
        -*)     usage ;;
        *)      break ;;
        esac
index f0549960fbd706f06fff5b2c602450917505a1ad..4c577eb835b877dd0766edbc5a603ee4e3db049a 100755 (executable)
@@ -120,26 +120,36 @@ git-apply --index "$PATCHFILE" || {
        O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
        rm -fr .patch-merge-*
 
+       if git-apply -z --index-info "$PATCHFILE" \
+               >.patch-merge-index-info 2>/dev/null &&
+               GIT_INDEX_FILE=.patch-merge-tmp-index \
+               git-update-index -z --index-info <.patch-merge-index-info &&
+               GIT_INDEX_FILE=.patch-merge-tmp-index \
+               git-write-tree >.patch-merge-tmp-base &&
+               (
+                       mkdir .patch-merge-tmp-dir &&
+                       cd .patch-merge-tmp-dir &&
+                       GIT_INDEX_FILE="../.patch-merge-tmp-index" \
+                       GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+                       git-apply $binary --index
+               ) <"$PATCHFILE"
+       then
+               echo Using index info to reconstruct a base tree...
+               mv .patch-merge-tmp-base .patch-merge-base
+               mv .patch-merge-tmp-index .patch-merge-index
+       else
        (
                N=10
 
-               # if the patch records the base tree...
-               sed -ne '
-                       /^diff /q
-                       /^applies-to: \([0-9a-f]*\)$/{
-                               s//\1/p
-                               q
-                       }
-               ' "$PATCHFILE"
-
-               # or hoping the patch is against our recent commits...
+               # Otherwise, try nearby trees that can be used to apply the
+               # patch.
                git-rev-list --max-count=$N HEAD
 
                # or hoping the patch is against known tags...
                git-ls-remote --tags .
        ) |
-       while read base junk
-       do
+           while read base junk
+           do
                # Try it if we have it as a tree.
                git-cat-file tree "$base" >/dev/null 2>&1 || continue
 
@@ -155,7 +165,8 @@ git-apply --index "$PATCHFILE" || {
                        mv ../.patch-merge-tmp-index ../.patch-merge-index &&
                        echo "$base" >../.patch-merge-base
                ) <"$PATCHFILE"  2>/dev/null && break
-       done
+           done
+       fi
 
        test -f .patch-merge-index &&
        his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
index d92993b94e4264d9c13547f8bc3a1d9c6c77017a..68838f3fad1d22ab4f14977434e9ce73365fb304 100755 (executable)
@@ -1,9 +1,19 @@
 #!/bin/sh
 . git-sh-setup
 
+sq() {
+       perl -e '
+               for (@ARGV) {
+                       s/'\''/'\'\\\\\'\''/g;
+                       print " '\''$_'\''";
+               }
+               print "\n";
+       ' "$@"
+}
+
 usage() {
     echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
-git bisect start               reset bisect state and start bisection.
+git bisect start [<pathspec>]  reset bisect state and start bisection.
 git bisect bad [<rev>]         mark <rev> a known-bad revision.
 git bisect good [<rev>...]     mark <rev>... known-good revisions.
 git bisect next                        find next bisection to test and check it out.
@@ -33,7 +43,6 @@ bisect_autostart() {
 }
 
 bisect_start() {
-        case "$#" in 0) ;; *) usage ;; esac
        #
        # Verify HEAD. If we were bisecting before this, reset to the
        # top-of-line master first!
@@ -57,7 +66,11 @@ bisect_start() {
        rm -f "$GIT_DIR/refs/heads/bisect"
        rm -rf "$GIT_DIR/refs/bisect/"
        mkdir "$GIT_DIR/refs/bisect"
-       echo "git-bisect start" >"$GIT_DIR/BISECT_LOG"
+       {
+           echo -n "git-bisect start"
+           sq "$@"
+       } >"$GIT_DIR/BISECT_LOG"
+       sq "$@" >"$GIT_DIR/BISECT_NAMES"
 }
 
 bisect_bad() {
@@ -121,7 +134,7 @@ bisect_next() {
        bad=$(git-rev-parse --verify refs/bisect/bad) &&
        good=$(git-rev-parse --sq --revs-only --not \
                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
-       rev=$(eval "git-rev-list --bisect $good $bad") || exit
+       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
        if [ -z "$rev" ]; then
            echo "$bad was both good and bad"
            exit 1
@@ -131,7 +144,7 @@ bisect_next() {
            git-diff-tree --pretty $rev
            exit 0
        fi
-       nr=$(eval "git-rev-list $rev $good" | wc -l) || exit
+       nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
        echo "Bisecting: $nr revisions left to test after this"
        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
        git checkout new-bisect || exit
@@ -142,7 +155,8 @@ bisect_next() {
 
 bisect_visualize() {
        bisect_next_check fail
-       gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*`
+       not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
+       eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
@@ -173,7 +187,8 @@ bisect_replay () {
                test "$bisect" = "git-bisect" || continue
                case "$command" in
                start)
-                       bisect_start
+                       cmd="bisect_start $rev"
+                       eval "$cmd"
                        ;;
                good)
                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
index 4cd5da16f74cee6e277d33bce74242496eca94fe..5306b2719ffbedfa077f4d5489ab46448ef5c186 100755 (executable)
@@ -1,6 +1,11 @@
 #!/bin/sh
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 usage () {
     echo >&2 "usage: $(basename $0)"' [-d <branch>] | [[-f] <branch> [start-point]]
@@ -12,8 +17,7 @@ If two arguments, create a new branch <branchname> based off of <start-point>.
     exit 1
 }
 
-headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
-       sed -e 's|^refs/heads/||')
+headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
 
 delete_branch () {
     option="$1"
@@ -114,4 +118,3 @@ then
        fi
 fi
 git update-ref "refs/heads/$branchname" $rev
-
index c09979a7a49e36b35e333b902cb0e0d689b693f4..699205eb6644aeb8668aa56abddf4f00d9af2642 100755 (executable)
@@ -73,7 +73,7 @@ while
        *,-n) no_checkout=yes ;;
        *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
         *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) 
-          local_shared=yes ;;
+          local_shared=yes; use_local=yes ;;
        *,-q|*,--quiet) quiet=-q ;;
        1,-u|1,--upload-pack) usage ;;
        *,-u|*,--upload-pack)
index d6e9a3221f1f63142d9490e9abdfc5c8ac8d6348..40c58efe0884a2431146bdd3dd12910033e5f182 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 
 dc </dev/null 2>/dev/null || {
        # This is not a real DC at all -- it just knows how
index b3ec84be698311b30e8c7c793873e6caa41daf71..b62e58341cd9d11fc893613949426126871531e2 100755 (executable)
@@ -7,7 +7,10 @@ rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
 flags=$(git-rev-parse --no-revs --flags --sq "$@")
 files=$(git-rev-parse --no-revs --no-flags --sq "$@")
 
-: ${flags:="'-M' '-p'"}
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 # I often say 'git diff --cached -p' and get scolded by git-diff-files, but
 # obviously I mean 'git diff --cached -p HEAD' in that case.
@@ -20,6 +23,21 @@ case "$rev" in
        esac
 esac
 
+# If we do not have --name-status, --name-only nor -r, default to -p.
+# If we do not have -B nor -C, default to -M.
+case " $flags " in
+*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* )
+       ;;
+*)
+       flags="$flags'-p' " ;;
+esac
+case " $flags " in
+*" '-"[BCM]* | *" '--find-copies-harder' "*)
+       ;; # something like -M50.
+*)
+       flags="$flags'-M' " ;;
+esac
+
 case "$rev" in
 ?*' '?*' '?*)
        echo >&2 "I don't understand"
index bc5687653150ff1e27720173b137d759d60c74c6..1eebe857c021495b92d9e39ec1193b85ab4e980e 100755 (executable)
@@ -5,6 +5,10 @@
 
 . git-sh-setup
 
+# Force diff to run in C locale.
+LANG=C LC_ALL=C
+export LANG LC_ALL
+
 usage () {
     echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
     [--check] [--signoff] [-<diff options>...]
@@ -202,7 +206,7 @@ process_one () {
            ;;
        esac
 
-       eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
+       eval "$(sed -ne "$whosepatchScript" $commsg)"
        test "$author,$au" = ",$me" || {
                mailScript="$mailScript"'
        a\
@@ -238,9 +242,8 @@ Date: '"$ad"
        echo
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
-       git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
        git-diff-tree -p $diff_opts "$commit"
-       echo "---"
+       echo "-- "
        echo "@@GIT_VERSION@@"
 
        case "$mbox" in
@@ -268,7 +271,7 @@ do
     file=`printf '%04d-%stxt' $i "$title"`
     if test '' = "$stdout"
     then
-           echo "$file"
+           echo "$file"
            process_one >"$outdir$file"
            if test t = "$check"
            then
@@ -279,7 +282,7 @@ do
                :
            fi
     else
-           echo >&2 "$file"
+           echo >&2 "$file"
            process_one
     fi
     i=`expr "$i" + 1`
index 9dd7430018f4bfb76c29e77e7fb9365acaa3bd9b..2beec2aa635d2876482294a162923a1dfd1572f6 100755 (executable)
@@ -1,7 +1,6 @@
 #!/bin/sh
 
-. git-sh-setup
-
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 laf="$GIT_DIR/lost-found"
 rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
 
index dc6a775a9be83cfd349773b07051cd256d5ea114..f69926862fba15c28933c6dd616065d331b9a89b 100755 (executable)
@@ -6,6 +6,11 @@ usage () {
     exit 1;
 }
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 while case "$#" in 0) break;; esac
 do
   case "$1" in
index bb58e22a18bdaefae3f8005e7b0a0cfaba4b4185..7adffdc7950baaf1da5b36913f78404c48c6d270 100755 (executable)
@@ -8,6 +8,11 @@
 LF='
 '
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 # The first parameters up to -- are merge bases; the rest are heads.
 bases= head= remotes= sep_seen=
 for arg
index c3eca8b3321df8dbf632fb687003737d8989d6f6..eafef770dbad16b48f25962cd67e8e380f3c8d48 100755 (executable)
@@ -26,7 +26,7 @@ case "${1:-.}${2:-.}${3:-.}" in
        fi
        if test -f "$4"; then
                rm -f -- "$4" &&
-               rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null
+               rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null || :
        fi &&
                exec git-update-index --remove -- "$4"
        ;;
@@ -58,6 +58,14 @@ case "${1:-.}${2:-.}${3:-.}" in
 # Modified in both, but differently.
 #
 "$1$2$3" | ".$2$3")
+
+       case ",$6,$7," in
+       *,120000,*)
+               echo "ERROR: $4: Not merging symbolic link changes."
+               exit 1
+               ;;
+       esac
+
        src2=`git-unpack-file $3`
        case "$1" in
        '')
@@ -79,10 +87,12 @@ case "${1:-.}${2:-.}${3:-.}" in
                ;;
        esac
 
-       # We reset the index to the first branch, making
-       # git-diff-file useful
-       git-update-index --add --cacheinfo "$6" "$2" "$4"
-               git-checkout-index -u -f -- "$4" &&
+       # Create the working tree file, with the correct permission bits.
+       # we can not rely on the fact that our tree has the path, because
+       # we allow the merge to be done in an unchecked-out working tree.
+       rm -f "$4" &&
+               git-cat-file blob "$2" >"$4" &&
+               case "$6" in *7??) chmod +x "$4" ;; esac &&
                merge "$4" "$orig" "$src2"
        ret=$?
        rm -f -- "$orig" "$src2"
index 01292335509bfd1460e12e0e1d86c4fcd1f265a4..b7fb0961e0758c566c459f51b6983f99e694ee4f 100755 (executable)
@@ -98,7 +98,7 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
 def getFilesAndDirs(tree):
     files = Set()
     dirs = Set()
-    out = runProgram(['git-ls-tree', '-r', '-z', tree])
+    out = runProgram(['git-ls-tree', '-r', '-z', '-t', tree])
     for l in out.split('\0'):
         m = getFilesRE.match(l)
         if m:
@@ -828,8 +828,6 @@ def processEntry(entry, branch1Name, branch2Name):
             if cacheOnly:
                 updateFile(False, sha, mode, path)
             else:
-                updateFileExt(aSha, aMode, path,
-                              updateCache=True, updateWd=False)
                 updateFileExt(sha, mode, path, updateCache=False, updateWd=True)
     else:
         die("ERROR: Fatal merge failure, shouldn't happen.")
index d352a3cf65e45e0da0a8ee1989e9acb522e132b5..a221daa7f4882892b29b19b156dfc31986fc8912 100755 (executable)
@@ -273,7 +273,8 @@ fi
 case "$best_strategy" in
 '')
        restorestate
-       die "No merge strategy handled the merge."
+       echo >&2 "No merge strategy handled the merge."
+       exit 2
        ;;
 "$wt_strategy")
        # We already have its result in the working tree.
index bf54c38413c02553bd314d24bfdadc4cdb23ccce..b6c0b4881845bf6a8d9681817fc540a2003b889e 100755 (executable)
 
 sub usage() {
        print <<EOT;
-$0 [-f] [-n] <source> <dest>
-$0 [-f] [-k] [-n] <source> ... <dest directory>
-
-In the first form, source must exist and be either a file,
-symlink or directory, dest must not exist. It renames source to dest.
-In the second form, the last argument has to be an existing
-directory; the given sources will be moved into this directory.
-
-Updates the git cache to reflect the change.
-Use "git commit" to make the change permanently.
-
-Options:
-  -f   Force renaming/moving, even if target exists
-  -k   Continue on error by skipping
-       not-existing or not revision-controlled source
-  -n   Do nothing; show what would happen
+$0 [-f] [-n] <source> <destination>
+$0 [-f] [-n] [-k] <source> ... <destination directory>
 EOT
        exit(1);
 }
 
-# Sanity checks:
-my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
-
-unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && 
-       -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
-    print "Git repository not found.";
-    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);
 
 our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
 getopts("hnfkv") || usage;
@@ -70,7 +50,7 @@ ()
        print "Error: moving to directory '"
            . $ARGV[$argCount-1]
            . "' not possible; not exisiting\n";
-       usage;
+       exit(1);
     }
     @srcArgs = ($ARGV[0]);
     @dstArgs = ($ARGV[1]);
@@ -122,7 +102,7 @@ ()
        }
     }
     
-    if (($bad eq "") && ($src eq $dstDir)) {
+    if (($bad eq "") && ($dst =~ /^$safesrc\//)) {
        $bad = "can not move directory '$src' into itself";
     }
 
@@ -148,7 +128,7 @@ ()
            next;
        }
        print "Error: $bad\n";
-       usage();
+       exit(1);
     }
     push @srcs, $src;
     push @dsts, $dst;
@@ -156,14 +136,22 @@ ()
 
 # 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) {
-       rename($src,$dst)
-           or die "rename failed: $!";
+       if (!rename($src,$dst)) {
+           $bad = "renaming '$src' failed: $!";
+           if ($opt_k) {
+               print "Warning: skipped: $bad\n";
+               $bad = "";
+               next;
+           }
+           last;
+       }
     }
 
     $safesrc = quotemeta($src);
@@ -187,20 +175,44 @@ ()
 }
 
 if ($opt_n) {
+    if (@changedfiles) {
        print "Changed  : ". join(", ", @changedfiles) ."\n";
+    }
+    if (@addedfiles) {
        print "Adding   : ". join(", ", @addedfiles) ."\n";
+    }
+    if (@deletedfiles) {
        print "Deleting : ". join(", ", @deletedfiles) ."\n";
-       exit(1);
+    }
 }
-       
-my $rc;
-if (scalar @changedfiles >0) {
-       $rc = system("git-update-index","--",@changedfiles);
-       die "git-update-index failed to update changed files with code $?\n" if $rc;
+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 (scalar @addedfiles >0) {
-       $rc = system("git-update-index","--add","--",@addedfiles);
-       die "git-update-index failed to add new names with code $?\n" if $rc;
+
+if ($bad ne "") {
+    print "Error: $bad\n";
+    exit(1);
 }
-$rc = system("git-update-index","--remove","--",@deletedfiles);
-die "git-update-index failed to remove old names with code $?\n" if $rc;
index 2bc3a12995ec5f18cf666289a6691fa2e4d3fb47..638ff0dbc01cbf87edf1af511d5caac5ee4f1d66 100755 (executable)
@@ -5,9 +5,25 @@
 
 . git-sh-setup
 
-# The other head is given
+# Make sure we do not have .dotest
+if mkdir .dotest
+then
+       rmdir .dotest
+else
+       echo >&2 '
+It seems that I cannot create a .dotest directory, and I wonder if you
+are in the middle of patch application or another rebase.  If that is not
+the case, please rm -fr .dotest and run me again.  I am stopping in case
+you still have something valuable there.'
+       exit 1
+fi
+
+# The other head is given.  Make sure it is valid.
 other=$(git-rev-parse --verify "$1^0") || exit
 
+# Make sure we have HEAD that is valid.
+head=$(git-rev-parse --verify "HEAD^0") || exit
+
 # The tree must be really really clean.
 git-update-index --refresh || exit
 diff=$(git-diff-index --cached --name-status -r HEAD)
@@ -23,6 +39,16 @@ case "$#" in
        git-checkout "$2" || exit
 esac
 
+# If the HEAD is a proper descendant of $other, we do not even need
+# to rebase.  Make sure we do not do needless rebase.  In such a
+# case, merge-base should be the same as "$other".
+mb=$(git-merge-base "$other" "$head")
+if test "$mb" = "$other"
+then
+       echo >&2 "Current branch `git-symbolic-ref HEAD` is up to date."
+       exit 0
+fi
+
 # Rewind the head to "$other"
 git-reset --hard "$other"
 git-format-patch -k --stdout --full-index "$other" ORIG_HEAD |
index 45d77c5bae9c055641c416676c1d783c73fc28bb..65868a91e5f88c41942da89b84f169ed4264615d 100755 (executable)
@@ -35,7 +35,7 @@
 sub usage() {
        print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from SVN
-       [-o branch-for-HEAD] [-h] [-v] [-l max_num_changes]
+       [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-s start_chg] [-m] [-M regex] [SVN_URL]
 END
@@ -126,8 +126,9 @@ sub file {
 package main;
 use URI;
 
-my $svn = $svn_url;
+our $svn = $svn_url;
 $svn .= "/$svn_dir" if defined $svn_dir;
+my $svn2 = SVNconn->new($svn);
 $svn = SVNconn->new($svn);
 
 my $lwp_ua;
@@ -198,7 +199,7 @@ ($$)
 my $maxnum = 0;
 my $last_rev = "";
 my $last_branch;
-my $current_rev = $opt_s-1;
+my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
        system("git-init-db");
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
@@ -254,7 +255,7 @@ ($$)
                my($num,$branch,$ref) = split;
                $branches{$branch}{$num} = $ref;
                $branches{$branch}{"LAST"} = $ref;
-               $current_rev = $num if $current_rev < $num;
+               $current_rev = $num+1 if $current_rev <= $num;
        }
        close($B);
 }
@@ -708,17 +709,17 @@ sub commit {
        print "DONE: $revision $dest $cid\n" if $opt_v;
 }
 
-my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
-sub _commit_all {
-       ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
+sub commit_all {
+       # Recursive use of the SVN connection does not work
+       local $svn = $svn2;
+
+       my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
        my %p;
        while(my($path,$action) = each %$changed_paths) {
                $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
        }
        $changed_paths = \%p;
-}
 
-sub commit_all {
        my %done;
        my @col;
        my $pref;
@@ -734,18 +735,12 @@ sub commit_all {
        }
 }
 
-while(++$current_rev <= $svn->{'maxrev'}) {
-       if (defined $opt_l) {
-               $opt_l--;
-               if ($opt_l < 0) {
-                       last;
-               }
-       }
-       my $pool=SVN::Pool->new;
-       $svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool);
-       $pool->clear;
-       commit_all();
-}
+$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'};
+print "Fetching from $current_rev to $opt_l ...\n" if $opt_v;
+
+my $pool=SVN::Pool->new;
+$svn->{'svn'}->get_log("/",$current_rev,$opt_l,0,1,1,\&commit_all,$pool);
+$pool->clear;
 
 
 unlink($git_index);
index 16efc5b70ac7ea4f0770530845e35091e71feab6..2435a75f7a9414bdceb1cef308a99918784f5421 100755 (executable)
@@ -1,13 +1,18 @@
 #!/bin/sh
 # Copyright (c) 2005 Linus Torvalds
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 
 usage () {
     echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"
     exit 1
 }
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 annotate=
 signed=
 force=
index 3c65f4a6b51707601bc8f66657e6e79cc33094fd..1f44da53496e880395286dafb431b9c3095df9f1 100755 (executable)
@@ -1,5 +1,11 @@
 #!/bin/sh
-. git-sh-setup
+
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 type="$(git-cat-file -t "$1" 2>/dev/null)" ||
        die "$1: no such object."
@@ -7,6 +13,9 @@ type="$(git-cat-file -t "$1" 2>/dev/null)" ||
 test "$type" = tag ||
        die "$1: cannot verify a non-tag object of type $type."
 
-git-cat-file tag "$1" > .tmp-vtag || exit 1
-cat .tmp-vtag | sed '/-----BEGIN PGP/Q' | gpg --verify .tmp-vtag - || exit 1
-rm -f .tmp-vtag
+git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
+cat "$GIT_DIR/.tmp-vtag" |
+sed '/-----BEGIN PGP/Q' |
+gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1
+rm -f "$GIT_DIR/.tmp-vtag"
+
diff --git a/git.c b/git.c
index 0b10b6e781fdf76a3822b15cfa304352aee812cc..619f25acf50154a2b1f4e5e2522b89416d1286f1 100644 (file)
--- a/git.c
+++ b/git.c
 # define PATH_MAX 4096
 #endif
 
+#ifdef NO_SETENV
+extern int gitsetenv(char *name, char *value, int overwrite);
+#endif
+
 static const char git_usage[] =
        "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
 
@@ -283,16 +287,21 @@ int main(int argc, char **argv, char **envp)
        len = strlen(git_command);
        prepend_to_path(git_command, len);
 
-       strncat(&git_command[len], "/git-", sizeof(git_command) - len);
-       len += 5;
-       strncat(&git_command[len], argv[i], sizeof(git_command) - len);
-
-       if (access(git_command, X_OK))
-               usage(exec_path, "'%s' is not a git-command", argv[i]);
+       len += snprintf(git_command + len, sizeof(git_command) - len,
+                       "/git-%s", argv[i]);
+       if (sizeof(git_command) <= len) {
+               fprintf(stderr, "git: command name given is too long (%d)\n", len);
+               exit(1);
+       }
 
        /* execve() can only ever return if it fails */
        execve(git_command, &argv[i], envp);
-       printf("Failed to run command '%s': %s\n", git_command, strerror(errno));
+
+       if (errno == ENOENT)
+               usage(exec_path, "'%s' is not a git-command", argv[i]);
+
+       fprintf(stderr, "Failed to run command '%s': %s\n",
+               git_command, strerror(errno));
 
        return 1;
 }
diff --git a/gitk b/gitk
index 3dd97e291ed18d45f998d37ae2f66a0bce3b60a9..a847ef69c7ae87c9035aff23af359e1832abceea 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -19,7 +19,7 @@ proc gitdir {} {
 proc getcommits {rargs} {
     global commits commfd phase canv mainfont env
     global startmsecs nextupdate ncmupdate
-    global ctext maincursor textcursor leftover
+    global ctext maincursor textcursor leftover gitencoding
 
     # check that we can find a .git directory somewhere...
     set gitdir [gitdir]
@@ -30,7 +30,7 @@ proc getcommits {rargs} {
     set commits {}
     set phase getcommits
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     if [catch {
        set parse_args [concat --default HEAD $rargs]
@@ -49,7 +49,7 @@ proc getcommits {rargs} {
        exit 1
     }
     set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf
+    fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding
     fileevent $commfd readable [list getcommitlines $commfd]
     $canv delete all
     $canv create text 3 3 -anchor nw -text "Reading commits..." \
@@ -74,9 +74,9 @@ proc getcommitlines {commfd}  {
        }
        if {[string range $err 0 4] == "usage"} {
            set err \
-{Gitk: error reading commits: bad arguments to git-rev-list.
-(Note: arguments to gitk are passed to git-rev-list
-to allow selection of commits to be displayed.)}
+               "Gitk: error reading commits: bad arguments to git-rev-list.\
+               (Note: arguments to gitk are passed to git-rev-list\
+               to allow selection of commits to be displayed.)"
        } else {
            set err "Error reading commits: $err"
        }
@@ -297,23 +297,26 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu gaudydiff mergemax
+    global rowctxmenu mergemax
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
     menu .bar.file
     .bar.file add command -label "Reread references" -command rereadrefs
     .bar.file add command -label "Quit" -command doquit
+    menu .bar.edit
+    .bar add cascade -label "Edit" -menu .bar.edit
+    .bar.edit add command -label "Preferences" -command doprefs
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
     . configure -menu .bar
 
     if {![info exists geometry(canv1)]} {
-       set geometry(canv1) [expr 45 * $charspc]
-       set geometry(canv2) [expr 30 * $charspc]
-       set geometry(canv3) [expr 15 * $charspc]
-       set geometry(canvh) [expr 25 * $linespc + 4]
+       set geometry(canv1) [expr {45 * $charspc}]
+       set geometry(canv2) [expr {30 * $charspc}]
+       set geometry(canv3) [expr {15 * $charspc}]
+       set geometry(canvh) [expr {25 * $linespc + 4}]
        set geometry(ctextw) 80
        set geometry(ctexth) 30
        set geometry(cflistw) 30
@@ -414,25 +417,19 @@ proc makewindow {} {
     .ctop.cdet add .ctop.cdet.left
 
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    if {$gaudydiff} {
-       $ctext tag conf hunksep -back blue -fore white
-       $ctext tag conf d0 -back "#ff8080"
-       $ctext tag conf d1 -back green
-    } else {
-       $ctext tag conf hunksep -fore blue
-       $ctext tag conf d0 -fore red
-       $ctext tag conf d1 -fore "#00a000"
-       $ctext tag conf m0 -fore red
-       $ctext tag conf m1 -fore blue
-       $ctext tag conf m2 -fore green
-       $ctext tag conf m3 -fore purple
-       $ctext tag conf m4 -fore brown
-       $ctext tag conf mmax -fore darkgrey
-       set mergemax 5
-       $ctext tag conf mresult -font [concat $textfont bold]
-       $ctext tag conf msep -font [concat $textfont bold]
-       $ctext tag conf found -back yellow
-    }
+    $ctext tag conf hunksep -fore blue
+    $ctext tag conf d0 -fore red
+    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf m0 -fore red
+    $ctext tag conf m1 -fore blue
+    $ctext tag conf m2 -fore green
+    $ctext tag conf m3 -fore purple
+    $ctext tag conf m4 -fore brown
+    $ctext tag conf mmax -fore darkgrey
+    set mergemax 5
+    $ctext tag conf mresult -font [concat $textfont bold]
+    $ctext tag conf msep -font [concat $textfont bold]
+    $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
@@ -533,7 +530,7 @@ proc click {w} {
 
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont
-    global stuffsaved findmergefiles gaudydiff maxgraphpct
+    global stuffsaved findmergefiles maxgraphpct
     global maxwidth
 
     if {$stuffsaved} return
@@ -543,15 +540,14 @@ proc savestuff {w} {
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
        puts $f [list set findmergefiles $findmergefiles]
-       puts $f [list set gaudydiff $gaudydiff]
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
-       puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
-       puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
-       puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
-       puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
+       puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
+       puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]"
+       puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
+       puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
        set wid [expr {([winfo width $ctext] - 8) \
                           / [font measure $textfont "0"]}]
        puts $f "set geometry(ctextw) $wid"
@@ -580,12 +576,12 @@ proc resizeclistpanes {win w} {
                set sash0 30
            }
            if {$sash1 < $sash0 + 20} {
-               set sash1 [expr $sash0 + 20]
+               set sash1 [expr {$sash0 + 20}]
            }
            if {$sash1 > $w - 10} {
-               set sash1 [expr $w - 10]
+               set sash1 [expr {$w - 10}]
                if {$sash0 > $sash1 - 20} {
-                   set sash0 [expr $sash1 - 20]
+                   set sash0 [expr {$sash1 - 20}]
                }
            }
        }
@@ -608,7 +604,7 @@ proc resizecdetpanes {win w} {
                set sash0 45
            }
            if {$sash0 > $w - 15} {
-               set sash0 [expr $w - 15]
+               set sash0 [expr {$w - 15}]
            }
        }
        $win sash place 0 $sash0 [lindex $s0 1]
@@ -819,9 +815,9 @@ proc drawcommitline {level} {
     }
     set x [xcoord $level $level $lineno]
     set y1 $canvy
-    set canvy [expr $canvy + $linespc]
+    set canvy [expr {$canvy + $linespc}]
     allcanvs conf -scrollregion \
-       [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
+       [list 0 0 0 [expr {$y1 + 0.5 * $linespc + 2}]]
     if {[info exists mainline($id)]} {
        lappend mainline($id) $x $y1
        if {$mainlinearrow($id) ne "none"} {
@@ -830,8 +826,8 @@ proc drawcommitline {level} {
     }
     drawlines $id 0 0
     set orad [expr {$linespc / 3}]
-    set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
-              [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
+    set t [$canv create oval [expr {$x - $orad}] [expr {$y1 - $orad}] \
+              [expr {$x + $orad - 1}] [expr {$y1 + $orad - 1}] \
               -fill $ofill -outline black -width 1]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
@@ -886,8 +882,8 @@ proc drawtags {id x xt y1} {
     }
 
     set delta [expr {int(0.5 * ($linespc - $lthickness))}]
-    set yt [expr $y1 - 0.5 * $linespc]
-    set yb [expr $yt + $linespc - 1]
+    set yt [expr {$y1 - 0.5 * $linespc}]
+    set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
     foreach tag $marks {
@@ -900,12 +896,12 @@ proc drawtags {id x xt y1} {
               -width $lthickness -fill black -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
-       set xl [expr $x + $delta]
-       set xr [expr $x + $delta + $wid + $lthickness]
+       set xl [expr {$x + $delta}]
+       set xr [expr {$x + $delta + $wid + $lthickness}]
        if {[incr ntags -1] >= 0} {
            # draw a tag
-           set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
-                      $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
+           set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
+                      $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
            set rowtextx($idline($id)) [expr {$xr + $linespc}]
@@ -916,7 +912,7 @@ proc drawtags {id x xt y1} {
            } else {
                set col "#ddddff"
            }
-           set xl [expr $xl - $delta/2]
+           set xl [expr {$xl - $delta/2}]
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
        }
@@ -1491,7 +1487,7 @@ proc drawgraph {} {
 
     if {$displayorder == {}} return
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     initgraph
     foreach id $displayorder {
@@ -1520,7 +1516,7 @@ proc drawrest {} {
     }
     drawmore 0
     set phase {}
-    set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
+    set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
     #puts "overall $drawmsecs ms for $numcommits commits"
     if {$redisplaying} {
        if {$stopped == 0 && [info exists selectedline]} {
@@ -1548,8 +1544,8 @@ proc findmatches {f} {
        set matches {}
        set i 0
        while {[set j [string first $foundstring $str $i]] >= 0} {
-           lappend matches [list $j [expr $j+$foundstrlen-1]]
-           set i [expr $j + $foundstrlen]
+           lappend matches [list $j [expr {$j+$foundstrlen-1}]]
+           set i [expr {$j + $foundstrlen}]
        }
     }
     return $matches
@@ -1630,7 +1626,7 @@ proc findselectline {l} {
        set matches [findmatches $f]
        foreach match $matches {
            set start [lindex $match 0]
-           set end [expr [lindex $match 1] + 1]
+           set end [expr {[lindex $match 1] + 1}]
            $ctext tag add found "1.0 + $start c" "1.0 + $end c"
        }
     }
@@ -1984,9 +1980,10 @@ proc markmatches {canv l str tag matches font} {
        set start [lindex $match 0]
        set end [lindex $match 1]
        if {$start > $end} continue
-       set xoff [font measure $font [string range $str 0 [expr $start-1]]]
-       set xlen [font measure $font [string range $str 0 [expr $end]]]
-       set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
+       set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
+       set xlen [font measure $font [string range $str 0 [expr {$end}]]]
+       set t [$canv create rect [expr {$x0+$xoff}] $y0 \
+                  [expr {$x0+$xlen+2}] $y1 \
                   -outline {} -tags matches -fill yellow]
        $canv lower $t
     }
@@ -2078,8 +2075,8 @@ proc selectline {l isnew} {
     set ytop [expr {$y - $linespc - 1}]
     set ybot [expr {$y + $linespc + 1}]
     set wnow [$canv yview]
-    set wtop [expr [lindex $wnow 0] * $ymax]
-    set wbot [expr [lindex $wnow 1] * $ymax]
+    set wtop [expr {[lindex $wnow 0] * $ymax}]
+    set wbot [expr {[lindex $wnow 1] * $ymax}]
     set wh [expr {$wbot - $wtop}]
     set newtop $wtop
     if {$ytop < $wtop} {
@@ -2105,7 +2102,7 @@ proc selectline {l isnew} {
        if {$newtop < 0} {
            set newtop 0
        }
-       allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
+       allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
     }
 
     if {$isnew} {
@@ -2164,7 +2161,7 @@ proc selectline {l isnew} {
     $cflist delete 0 end
     $cflist insert end "Comments"
     if {$nparents($id) == 1} {
-       startdiff [concat $id $parents($id)]
+       startdiff $id
     } elseif {$nparents($id) > 1} {
        mergediff $id
     }
@@ -2173,7 +2170,7 @@ proc selectline {l isnew} {
 proc selnextline {dir} {
     global selectedline
     if {![info exists selectedline]} return
-    set l [expr $selectedline + $dir]
+    set l [expr {$selectedline + $dir}]
     unmarkmatches
     selectline $l 1
 }
@@ -2273,12 +2270,12 @@ proc contmergediff {ids} {
     # diff the child against each of the parents, and diff
     # each of the parents against the GCA.
     while 1 {
-       if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
-           set ids [list [lindex $ids 1] $diffmergegca]
+       if {[lindex $ids 1] == $diffmergeid && $diffmergegca ne {}} {
+           set ids [list $diffmergegca [lindex $ids 0]]
        } else {
            if {[incr diffpindex] >= $nparents($diffmergeid)} break
            set p [lindex $parents($diffmergeid) $diffpindex]
-           set ids [list $diffmergeid $p]
+           set ids [list $p $diffmergeid]
        }
        if {![info exists treediffs($ids)]} {
            set diffids $ids
@@ -2296,8 +2293,8 @@ proc contmergediff {ids} {
     if {$diffmergegca ne {}} {
        set files {}
        foreach p $parents($diffmergeid) {
-           set gcadiffs $treediffs([list $p $diffmergegca])
-           foreach f $treediffs([list $diffmergeid $p]) {
+           set gcadiffs $treediffs([list $diffmergegca $p])
+           foreach f $treediffs([list $p $diffmergeid]) {
                if {[lsearch -exact $files $f] < 0
                    && [lsearch -exact $gcadiffs $f] >= 0} {
                    lappend files $f
@@ -2310,7 +2307,7 @@ proc contmergediff {ids} {
        set files $treediffs([list $diffmergeid $p])
        for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
            set p [lindex $parents($diffmergeid) $i]
-           set df $treediffs([list $diffmergeid $p])
+           set df $treediffs([list $p $diffmergeid])
            set nf {}
            foreach f $files {
                if {[lsearch -exact $df $f] >= 0} {
@@ -2787,8 +2784,7 @@ proc gettreediffs {ids} {
     global treediff parents treepending
     set treepending $ids
     set treediff {}
-    set id [lindex $ids 0]
-    if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return
+    if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]}] return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
 }
@@ -2821,9 +2817,8 @@ proc getblobdiffs {ids} {
     global diffopts blobdifffd diffids env curdifftag curtagstart
     global difffilestart nextupdate diffinhdr treediffs
 
-    set id [lindex $ids 0]
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [list | git-diff-tree --no-commit-id -r -p -C $id]
+    set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
@@ -2842,7 +2837,6 @@ proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
     global nextupdate diffinhdr treediffs
-    global gaudydiff
 
     set n [gets $bdf line]
     if {$n < 0} {
@@ -2891,26 +2885,14 @@ proc getblobdiffline {bdf ids} {
        set diffinhdr 0
     } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
                   $line match f1l f1c f2l f2c rest]} {
-       if {$gaudydiff} {
-           $ctext insert end "\t" hunksep
-           $ctext insert end "    $f1l    " d0 "    $f2l    " d1
-           $ctext insert end "    $rest \n" hunksep
-       } else {
-           $ctext insert end "$line\n" hunksep
-       }
+       $ctext insert end "$line\n" hunksep
        set diffinhdr 0
     } else {
        set x [string range $line 0 0]
        if {$x == "-" || $x == "+"} {
            set tag [expr {$x == "+"}]
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n" d$tag
        } elseif {$x == " "} {
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n"
        } elseif {$diffinhdr || $x == "\\"} {
            # e.g. "\ No newline at end of file"
@@ -2966,8 +2948,8 @@ proc setcoords {} {
 
     set linespc [font metrics $mainfont -linespace]
     set charspc [font measure $mainfont "m"]
-    set canvy0 [expr 3 + 0.5 * $linespc]
-    set canvx0 [expr 3 + 0.5 * $linespc]
+    set canvy0 [expr {3 + 0.5 * $linespc}]
+    set canvx0 [expr {3 + 0.5 * $linespc}]
     set lthickness [expr {int($linespc / 9) + 1}]
     set xspc1(0) $linespc
     set xspc2 $linespc
@@ -3340,7 +3322,7 @@ proc doseldiff {oldid newid} {
     $ctext conf -state disabled
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
-    startdiff [list $newid $oldid]
+    startdiff [list $oldid $newid]
 }
 
 proc mkpatch {} {
@@ -3635,33 +3617,94 @@ proc doquit {} {
     destroy .
 }
 
-proc formatdate {d} {
-    global hours nhours tfd fastdate
+proc doprefs {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
 
-    if {!$fastdate} {
-       return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+    set top .gitkprefs
+    set prefstop $top
+    if {[winfo exists $top]} {
+       raise $top
+       return
     }
-    set hr [expr {$d / 3600}]
-    set ms [expr {$d % 3600}]
-    if {![info exists hours($hr)]} {
-       set hours($hr) [clock format $d -format "%Y-%m-%d %H"]
-       set nhours($hr) 0
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set oldprefs($v) [set $v]
     }
-    incr nhours($hr)
-    set minsec [format "%.2d:%.2d" [expr {$ms/60}] [expr {$ms%60}]]
-    return "$hours($hr):$minsec"
+    toplevel $top
+    wm title $top "Gitk preferences"
+    label $top.ldisp -text "Commit list display options"
+    grid $top.ldisp - -sticky w -pady 10
+    label $top.spacer -text " "
+    label $top.maxwidthl -text "Maximum graph width (lines)" \
+       -font optionfont
+    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
+    label $top.maxpctl -text "Maximum graph width (% of pane)" \
+       -font optionfont
+    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $top.maxpctl $top.maxpct -sticky w
+    checkbutton $top.findm -variable findmergefiles
+    label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
+       -font optionfont
+    grid $top.findm $top.findml - -sticky w
+    label $top.ddisp -text "Diff display options"
+    grid $top.ddisp - -sticky w -pady 10
+    label $top.diffoptl -text "Options for diff program" \
+       -font optionfont
+    entry $top.diffopt -width 20 -textvariable diffopts
+    grid x $top.diffoptl $top.diffopt -sticky w
+    frame $top.buts
+    button $top.buts.ok -text "OK" -command prefsok
+    button $top.buts.can -text "Cancel" -command prefscan
+    grid $top.buts.ok $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - - -pady 10 -sticky ew
+}
+
+proc prefscan {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
+
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set $v $oldprefs($v)
+    }
+    catch {destroy $prefstop}
+    unset prefstop
+}
+
+proc prefsok {} {
+    global maxwidth maxgraphpct
+    global oldprefs prefstop
+
+    catch {destroy $prefstop}
+    unset prefstop
+    if {$maxwidth != $oldprefs(maxwidth)
+       || $maxgraphpct != $oldprefs(maxgraphpct)} {
+       redisplay
+    }
+}
+
+proc formatdate {d} {
+    return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
 }
 
 # defaults...
 set datemode 0
-set boldnames 0
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
+set gitencoding ""
+catch {
+    set gitencoding [exec git-repo-config --get i18n.commitencoding]
+}
+if {$gitencoding == ""} {
+       set gitencoding "utf-8"
+}
+
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set findmergefiles 0
-set gaudydiff 0
 set maxgraphpct 50
 set maxwidth 16
 set revlistorder 0
@@ -3672,15 +3715,13 @@ set colors {green red blue magenta darkgrey brown orange}
 catch {source ~/.gitk}
 
 set namefont $mainfont
-if {$boldnames} {
-    lappend namefont bold
-}
+
+font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
 foreach arg $argv {
     switch -regexp -- $arg {
        "^$" { }
-       "^-b" { set boldnames 1 }
        "^-d" { set datemode 1 }
        "^-r" { set revlistorder 1 }
        default {
index c8c9adb3aa789729e3c53a0d9ff1135367e93ee0..ccba11cb32fb6989c9c0365097de2fa39197de25 100644 (file)
@@ -29,6 +29,8 @@ int main(int argc, char **argv)
        int i;
        const char *type = "blob";
        int write_object = 0;
+       const char *prefix = NULL;
+       int prefix_length = -1;
 
        for (i = 1 ; i < argc; i++) {
                if (!strcmp(argv[i], "-t")) {
@@ -36,10 +38,20 @@ int main(int argc, char **argv)
                                die(hash_object_usage);
                        type = argv[i];
                }
-               else if (!strcmp(argv[i], "-w"))
+               else if (!strcmp(argv[i], "-w")) {
+                       if (prefix_length < 0) {
+                               prefix = setup_git_directory();
+                               prefix_length = prefix ? strlen(prefix) : 0;
+                       }
                        write_object = 1;
-               else
-                       hash_object(argv[i], type, write_object);
+               }
+               else {
+                       const char *arg = argv[i];
+                       if (0 <= prefix_length)
+                               arg = prefix_filename(prefix, prefix_length,
+                                                     arg);
+                       hash_object(arg, type, write_object);
+               }
        }
        return 0;
 }
index 435317342be0d31088f5aae5b337e090b40199b5..ad59f1cce61d73fa832828aabb880f082debf94d 100644 (file)
@@ -922,6 +922,8 @@ int main(int argc, char **argv)
        int arg = 1;
        int rc = 0;
 
+       setup_git_directory();
+
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
                        get_tree = 1;
index 76c788673e0a02c723d99976d3e605531341c7a9..fe925609b4024119c6171dd32350a6570bea8516 100644 (file)
@@ -784,7 +784,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
                                        strtol(ctx->cdata + 7, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
                        if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
-                               lock->token = xmalloc(strlen(ctx->cdata - 15));
+                               lock->token = xmalloc(strlen(ctx->cdata) - 15);
                                strcpy(lock->token, ctx->cdata + 16);
                        }
                }
@@ -1008,9 +1008,7 @@ static int unlock_remote(struct active_lock *lock)
        if (lock->owner != NULL)
                free(lock->owner);
        free(lock->url);
-/* Freeing the token causes a segfault...
        free(lock->token);
-*/
        free(lock);
 
        return rc;
@@ -1239,6 +1237,7 @@ int main(int argc, char **argv)
        int rc = 0;
        int i;
 
+       setup_git_directory();
        setup_ident();
 
        remote = xmalloc(sizeof(*remote));
@@ -1273,6 +1272,9 @@ int main(int argc, char **argv)
                break;
        }
 
+       if (!remote->url)
+               usage(http_push_usage);
+
        memset(remote_dir_exists, 0, 256);
 
        http_init();
index bd88291b0efd3eb9c46195d413db1aba820db2dd..8195b6842ea4eb1f6e3e0d72f8c5900b4d7e2a8a 100644 (file)
--- a/init-db.c
+++ b/init-db.c
@@ -132,6 +132,23 @@ static void copy_templates(const char *git_dir, int len, char *template_dir)
                return;
        }
 
+       /* Make sure that template is from the correct vintage */
+       strcpy(template_path + template_len, "config");
+       repository_format_version = 0;
+       git_config_from_file(check_repository_format_version,
+                            template_path);
+       template_path[template_len] = 0;
+
+       if (repository_format_version &&
+           repository_format_version != GIT_REPO_VERSION) {
+               fprintf(stderr, "warning: not copying templates of "
+                       "a wrong format version %d from '%s'\n",
+                       repository_format_version,
+                       template_dir);
+               closedir(dir);
+               return;
+       }
+
        memcpy(path, git_dir, len);
        path[len] = 0;
        copy_templates_1(path, len,
@@ -140,12 +157,13 @@ static void copy_templates(const char *git_dir, int len, char *template_dir)
        closedir(dir);
 }
 
-static void create_default_files(const char *git_dir,
-                                char *template_path)
+static void create_default_files(const char *git_dir, char *template_path)
 {
        unsigned len = strlen(git_dir);
        static char path[PATH_MAX];
        unsigned char sha1[20];
+       struct stat st1;
+       char repo_version_string[10];
 
        if (len > sizeof(path)-50)
                die("insane git directory %s", git_dir);
@@ -164,6 +182,15 @@ static void create_default_files(const char *git_dir,
        strcpy(path + len, "refs/tags");
        safe_create_dir(path);
 
+       /* First copy the templates -- we might have the default
+        * config file there, in which case we would want to read
+        * from it after installing.
+        */
+       path[len] = 0;
+       copy_templates(path, len, template_path);
+
+       git_config(git_default_config);
+
        /*
         * Create the default symlink from ".git/HEAD" to the "master"
         * branch, if it does not exist yet.
@@ -173,44 +200,22 @@ static void create_default_files(const char *git_dir,
                if (create_symref(path, "refs/heads/master") < 0)
                        exit(1);
        }
-       path[len] = 0;
-       copy_templates(path, len, template_path);
 
-       /*
-        * Find out if we can trust the executable bit.
-        */
-       safe_create_dir(path);
+       /* This forces creation of new config file */
+       sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
+       git_config_set("core.repositoryformatversion", repo_version_string);
+
+       path[len] = 0;
        strcpy(path + len, "config");
-       if (access(path, R_OK) < 0) {
-               static const char contents[] =
-                       "#\n"
-                       "# This is the config file\n"
-                       "#\n"
-                       "\n"
-                       "; core variables\n"
-                       "[core]\n"
-                       "       ; Don't trust file modes\n"
-                       "       filemode = false\n"
-                       "\n";
-               FILE *config = fopen(path, "w");
-               struct stat st;
-
-               if (!config)
-                       die("Can not write to %s?", path);
-
-               fwrite(contents, sizeof(contents)-1, 1, config);
-
-               fclose(config);
-
-               if (!lstat(path, &st)) {
-                       struct stat st2;
-                       if (!chmod(path, st.st_mode ^ S_IXUSR) &&
-                                       !lstat(path, &st2) &&
-                                       st.st_mode != st2.st_mode)
-                               unlink(path);
-                       else
-                               fprintf(stderr, "Ignoring file modes\n");
-               }
+
+       /* Check filemode trustability */
+       if (!lstat(path, &st1)) {
+               struct stat st2;
+               int filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+                               !lstat(path, &st2) &&
+                               st1.st_mode != st2.st_mode);
+               git_config_set("core.filemode",
+                              filemode ? "true" : "false");
        }
 }
 
@@ -249,6 +254,14 @@ int main(int argc, char **argv)
                fprintf(stderr, "defaulting to local storage area\n");
        }
        safe_create_dir(git_dir);
+
+       /* Check to see if the repository version is right.
+        * Note that a newly created repository does not have
+        * config file, so this will not fail.  What we are catching
+        * is an attempt to reinitialize new repository with an old tool.
+        */
+       check_repository_format();
+
        create_default_files(git_dir, template_dir);
 
        /*
index 0931109143e0ce1c2f03f654b3ab67965220011a..fa9e697fd3fa8fe305184f78fdf500532c14ab63 100644 (file)
@@ -207,6 +207,8 @@ int main(int argc, char **argv)
        char *commit_id;
        int arg = 1;
 
+       setup_git_directory();
+
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't')
                        get_tree = 1;
index db2288aeee61bf64f30fdcc4b11e91c0cd3038a0..f3f1a6a6633881bf43887731fdcbe08af9ad2f4a 100644 (file)
@@ -344,7 +344,7 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
                return;
 
        fputs(tag, stdout);
-       write_name_quoted("", ent->name + offset, line_terminator, stdout);
+       write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
        putchar(line_terminator);
 }
 
@@ -433,7 +433,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
 
        if (!show_stage) {
                fputs(tag, stdout);
-               write_name_quoted("", ce->name + offset, line_terminator, stdout);
+               write_name_quoted("", 0, ce->name + offset,
+                                 line_terminator, stdout);
                putchar(line_terminator);
        }
        else {
@@ -442,7 +443,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       ntohl(ce->ce_mode),
                       sha1_to_hex(ce->sha1),
                       ce_stage(ce));
-               write_name_quoted("", ce->name + offset, line_terminator, stdout);
+               write_name_quoted("", 0, ce->name + offset,
+                                 line_terminator, stdout);
                putchar(line_terminator);
        }
 }
index d9f15e349cb833401eea38d21fb050b10f9678d4..dae377d99585d4e6a176f85f0101d8ae29668f2f 100644 (file)
--- a/ls-tree.c
+++ b/ls-tree.c
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
 #define LS_TREE_ONLY 2
+#define LS_SHOW_TREES 4
+#define LS_NAME_ONLY 8
 static int ls_options = 0;
+const char **pathspec;
 
-static struct tree_entry_list root_entry;
-
-static void prepare_root(unsigned char *sha1)
-{
-       unsigned char rsha[20];
-       unsigned long size;
-       void *buf;
-       struct tree *root_tree;
-
-       buf = read_object_with_reference(sha1, "tree", &size, rsha);
-       free(buf);
-       if (!buf)
-               die("Could not read %s", sha1_to_hex(sha1));
-
-       root_tree = lookup_tree(rsha);
-       if (!root_tree)
-               die("Could not read %s", sha1_to_hex(sha1));
-
-       /* Prepare a fake entry */
-       root_entry.directory = 1;
-       root_entry.executable = root_entry.symlink = 0;
-       root_entry.mode = S_IFDIR;
-       root_entry.name = "";
-       root_entry.item.tree = root_tree;
-       root_entry.parent = NULL;
-}
+static const char ls_tree_usage[] =
+       "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [path...]";
 
-static int prepare_children(struct tree_entry_list *elem)
+static int show_recursive(const char *base, int baselen, const char *pathname)
 {
-       if (!elem->directory)
-               return -1;
-       if (!elem->item.tree->object.parsed) {
-               struct tree_entry_list *e;
-               if (parse_tree(elem->item.tree))
-                       return -1;
-               /* Set up the parent link */
-               for (e = elem->item.tree->entries; e; e = e->next)
-                       e->parent = elem;
+       const char **s;
+
+       if (ls_options & LS_RECURSIVE)
+               return 1;
+
+       s = pathspec;
+       if (!s)
+               return 0;
+
+       for (;;) {
+               const char *spec = *s++;
+               int len, speclen;
+
+               if (!spec)
+                       return 0;
+               if (strncmp(base, spec, baselen))
+                       continue;
+               len = strlen(pathname);
+               spec += baselen;
+               speclen = strlen(spec);
+               if (speclen <= len)
+                       continue;
+               if (memcmp(pathname, spec, len))
+                       continue;
+               return 1;
        }
-       return 0;
 }
 
-static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
+static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-       const char *next, *slash;
-       int len;
-       struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
-
-       *(pathbuf) = '\0';
-
-       /* Find tree element, descending from root, that
-        * corresponds to the named path, lazily expanding
-        * the tree if possible.
-        */
-
-       while (path) {
-               /* The fact we still have path means that the caller
-                * wants us to make sure that elem at this point is a
-                * directory, and possibly descend into it.  Even what
-                * is left is just trailing slashes, we loop back to
-                * here, and this call to prepare_children() will
-                * catch elem not being a tree.  Nice.
-                */
-               if (prepare_children(elem))
-                       return NULL;
-
-               slash = strchr(path, '/');
-               if (!slash) {
-                       len = strlen(path);
-                       next = NULL;
-               }
-               else {
-                       next = slash + 1;
-                       len = slash - path;
+       int retval = 0;
+       const char *type = "blob";
+
+       if (S_ISDIR(mode)) {
+               if (show_recursive(base, baselen, pathname)) {
+                       retval = READ_TREE_RECURSIVE;
+                       if (!(ls_options & LS_SHOW_TREES))
+                               return retval;
                }
-               if (len) {
-                       if (oldelem) {
-                               pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
-                       }
-
-                       /* (len == 0) if the original path was "drivers/char/"
-                        * and we have run already two rounds, having elem
-                        * pointing at the drivers/char directory.
-                        */
-                       elem = elem->item.tree->entries;
-                       while (elem) {
-                               if ((strlen(elem->name) == len) &&
-                                   !strncmp(elem->name, path, len)) {
-                                       /* found */
-                                       break;
-                               }
-                               elem = elem->next;
-                       }
-                       if (!elem)
-                               return NULL;
-
-                       oldelem = elem;
-               }
-               path = next;
-       }
-
-       return elem;
-}
-
-static const char *entry_type(struct tree_entry_list *e)
-{
-       return (e->directory ? "tree" : "blob");
-}
-
-static const char *entry_hex(struct tree_entry_list *e)
-{
-       return sha1_to_hex(e->directory
-                          ? e->item.tree->object.sha1
-                          : e->item.blob->object.sha1);
-}
-
-/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-
-static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
-{
-       int oldlen = strlen(pathbuf);
-
-       if (e != &root_entry)
-               sprintf(pathbuf + oldlen, "%s/", e->name);
-
-       if (prepare_children(e))
-               die("internal error: ls-tree show_children called with non tree");
-       e = e->item.tree->entries;
-       while (e) {
-               show_entry(e, level, pathbuf);
-               e = e->next;
-       }
-
-       pathbuf[oldlen] = '\0';
-
-       return 0;
-}
-
-static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
-{
-       int err = 0; 
-
-       if (e != &root_entry) {
-               printf("%06o %s %s      ",
-                      e->mode, entry_type(e), entry_hex(e));
-               write_name_quoted(pathbuf, e->name, line_termination, stdout);
-               putchar(line_termination);
-       }
-
-       if (e->directory) {
-               /* If this is a directory, we have the following cases:
-                * (1) This is the top-level request (explicit path from the
-                *     command line, or "root" if there is no command line).
-                *  a. Without any flag.  We show direct children.  We do not 
-                *     recurse into them.
-                *  b. With -r.  We do recurse into children.
-                *  c. With -d.  We do not recurse into children.
-                * (2) We came here because our caller is either (1-a) or
-                *     (1-b).
-                *  a. Without any flag.  We do not show our children (which
-                *     are grandchildren for the original request).
-                *  b. With -r.  We continue to recurse into our children.
-                *  c. With -d.  We should not have come here to begin with.
-                */
-               if (level == 0 && !(ls_options & LS_TREE_ONLY))
-                       /* case (1)-a and (1)-b */
-                       err = err | show_children(e, level+1, pathbuf);
-               else if (level && ls_options & LS_RECURSIVE)
-                       /* case (2)-b */
-                       err = err | show_children(e, level+1, pathbuf);
+               type = "tree";
        }
-       return err;
+       else if (ls_options & LS_TREE_ONLY)
+               return 0;
+
+       if (!(ls_options & LS_NAME_ONLY))
+               printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1));
+       write_name_quoted(base, baselen, pathname, line_termination, stdout);
+       putchar(line_termination);
+       return retval;
 }
 
-static int list_one(const char *path)
+int main(int argc, const char **argv)
 {
-       int err = 0;
-       char pathbuf[MAXPATHLEN + 1];
-       struct tree_entry_list *e = find_entry(path, pathbuf);
-       if (!e) {
-               /* traditionally ls-tree does not complain about
-                * missing path.  We may change this later to match
-                * what "/bin/ls -a" does, which is to complain.
-                */
-               return err;
-       }
-       err = err | show_entry(e, 0, pathbuf);
-       return err;
-}
-
-static int list(char **path)
-{
-       int i;
-       int err = 0;
-       for (i = 0; path[i]; i++)
-               err = err | list_one(path[i]);
-       return err;
-}
-
-static const char ls_tree_usage[] =
-       "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
-
-int main(int argc, char **argv)
-{
-       static char *path0[] = { "", NULL };
-       char **path;
+       const char *prefix;
        unsigned char sha1[20];
+       char *buf;
+       unsigned long size;
 
+       prefix = setup_git_directory();
        while (1 < argc && argv[1][0] == '-') {
                switch (argv[1][1]) {
                case 'z':
@@ -233,20 +91,36 @@ int main(int argc, char **argv)
                case 'd':
                        ls_options |= LS_TREE_ONLY;
                        break;
+               case 't':
+                       ls_options |= LS_SHOW_TREES;
+                       break;
+               case '-':
+                       if (!strcmp(argv[1]+2, "name-only") ||
+                           !strcmp(argv[1]+2, "name-status")) {
+                               ls_options |= LS_NAME_ONLY;
+                               break;
+                       }
+                       /* otherwise fallthru */
                default:
                        usage(ls_tree_usage);
                }
                argc--; argv++;
        }
+       /* -d -r should imply -t, but -d by itself should not have to. */
+       if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
+           ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
+               ls_options |= LS_SHOW_TREES;
 
        if (argc < 2)
                usage(ls_tree_usage);
        if (get_sha1(argv[1], sha1) < 0)
                usage(ls_tree_usage);
 
-       path = (argc == 2) ? path0 : (argv + 2);
-       prepare_root(sha1);
-       if (list(path) < 0)
-               die("list failed");
+       pathspec = get_pathspec(prefix, argv + 2);
+       buf = read_object_with_reference(sha1, "tree", &size, NULL);
+       if (!buf)
+               die("not a tree object");
+       read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree);
+
        return 0;
 }
index cb853df993f37d74576e308008379d709361a447..890e3487adf65b937f9a9d1ec82d59c746bf62de 100644 (file)
@@ -8,6 +8,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <iconv.h>
+#include "cache.h"
 
 #ifdef NO_STRCASESTR
 extern char *gitstrcasestr(const char *haystack, const char *needle);
@@ -16,7 +17,7 @@ extern char *gitstrcasestr(const char *haystack, const char *needle);
 static FILE *cmitmsg, *patchfile;
 
 static int keep_subject = 0;
-static int metainfo_utf8 = 0;
+static char *metainfo_charset = NULL;
 static char line[1000];
 static char date[1000];
 static char name[1000];
@@ -441,29 +442,38 @@ static int decode_b_segment(char *in, char *ot, char *ep)
 
 static void convert_to_utf8(char *line, char *charset)
 {
-       if (*charset) {
-               char *in, *out;
-               size_t insize, outsize, nrc;
-               char outbuf[4096]; /* cheat */
-               iconv_t conv = iconv_open("utf-8", charset);
-
-               if (conv == (iconv_t) -1) {
-                       fprintf(stderr, "cannot convert from %s to utf-8\n",
-                               charset);
+       char *in, *out;
+       size_t insize, outsize, nrc;
+       char outbuf[4096]; /* cheat */
+       static char latin_one[] = "latin-1";
+       char *input_charset = *charset ? charset : latin_one;
+       iconv_t conv = iconv_open(metainfo_charset, input_charset);
+
+       if (conv == (iconv_t) -1) {
+               static int warned_latin1_once = 0;
+               if (input_charset != latin_one) {
+                       fprintf(stderr, "cannot convert from %s to %s\n",
+                               input_charset, metainfo_charset);
                        *charset = 0;
-                       return;
                }
-               in = line;
-               insize = strlen(in);
-               out = outbuf;
-               outsize = sizeof(outbuf);
-               nrc = iconv(conv, &in, &insize, &out, &outsize);
-               iconv_close(conv);
-               if (nrc == (size_t) -1)
-                       return;
-               *out = 0;
-               strcpy(line, outbuf);
+               else if (!warned_latin1_once) {
+                       warned_latin1_once = 1;
+                       fprintf(stderr, "tried to convert from %s to %s, "
+                               "but your iconv does not work with it.\n",
+                               input_charset, metainfo_charset);
+               }
+               return;
        }
+       in = line;
+       insize = strlen(in);
+       out = outbuf;
+       outsize = sizeof(outbuf);
+       nrc = iconv(conv, &in, &insize, &out, &outsize);
+       iconv_close(conv);
+       if (nrc == (size_t) -1)
+               return;
+       *out = 0;
+       strcpy(line, outbuf);
 }
 
 static void decode_header_bq(char *it)
@@ -511,7 +521,7 @@ static void decode_header_bq(char *it)
                }
                if (sz < 0)
                        return;
-               if (metainfo_utf8)
+               if (metainfo_charset)
                        convert_to_utf8(piecebuf, charset_q);
                strcpy(out, piecebuf);
                out += strlen(out);
@@ -590,7 +600,7 @@ static int handle_commit_msg(void)
                 * normalize the log message to UTF-8.
                 */
                decode_transfer_encoding(line);
-               if (metainfo_utf8)
+               if (metainfo_charset)
                        convert_to_utf8(line, charset);
                fputs(line, cmitmsg);
        } while (fgets(line, sizeof(line), stdin) != NULL);
@@ -707,27 +717,29 @@ static void handle_body(void)
 }
 
 static const char mailinfo_usage[] =
-       "git-mailinfo [-k] [-u] msg patch <mail >info";
-
-static void usage(void) {
-       fprintf(stderr, "%s\n", mailinfo_usage);
-       exit(1);
-}
+       "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
 
 int main(int argc, char **argv)
 {
+       /* NEEDSWORK: might want to do the optional .git/ directory
+        * discovery
+        */
+       git_config(git_default_config);
+
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-k"))
                        keep_subject = 1;
                else if (!strcmp(argv[1], "-u"))
-                       metainfo_utf8 = 1;
+                       metainfo_charset = git_commit_encoding;
+               else if (!strncmp(argv[1], "--encoding=", 11))
+                       metainfo_charset = argv[1] + 11;
                else
-                       usage();
+                       usage(mailinfo_usage);
                argc--; argv++;
        }
 
        if (argc != 3)
-               usage();
+               usage(mailinfo_usage);
        cmitmsg = fopen(argv[1], "w");
        if (!cmitmsg) {
                perror(argv[1]);
index 751c3c281b8803ccb83a9de99c3ab5d63039c377..e73fca7453e8141b3f371a1aeba483c9ed946ccc 100644 (file)
@@ -236,6 +236,8 @@ int main(int argc, char **argv)
        struct commit *rev1, *rev2;
        unsigned char rev1key[20], rev2key[20];
 
+       setup_git_directory();
+
        while (1 < argc && argv[1][0] == '-') {
                char *arg = argv[1];
                if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
index 727527fd59480a11a563108fa5d55a2696a40e98..024196e7ac53cb9ad09b38ea646bad81aff33306 100644 (file)
@@ -102,6 +102,7 @@ int main(int argc, char **argv)
        if (argc < 3)
                usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
 
+       setup_git_directory();
        read_cache();
 
        i = 1;
diff --git a/mktag.c b/mktag.c
index 585677eb83cd0fd21963d21291a6af2b54fc9c84..97e270a5761f071aeb3666b595b5cd6a8cbd4098 100644 (file)
--- a/mktag.c
+++ b/mktag.c
@@ -111,6 +111,8 @@ int main(int argc, char **argv)
        if (argc != 1)
                usage("cat <signaturefile> | git-mktag");
 
+       setup_git_directory();
+
        // Read the signature
        size = 0;
        for (;;) {
index 817e36b793aa1560d43b27682afe6631564e5738..65333d4166da845d8594d2dc397c0026eebe2421 100644 (file)
@@ -21,7 +21,7 @@ static void name_rev(struct commit *commit,
 {
        struct rev_name *name = (struct rev_name *)commit->object.util;
        struct commit_list *parents;
-       int parent_number = 0;
+       int parent_number = 1;
 
        if (!commit->object.parsed)
                parse_commit(commit);
@@ -56,7 +56,7 @@ static void name_rev(struct commit *commit,
        for (parents = commit->parents;
                        parents;
                        parents = parents->next, parent_number++) {
-               if (parent_number > 0) {
+               if (parent_number > 1) {
                        char *new_name = xmalloc(strlen(tip_name)+8);
 
                        if (generation > 0)
@@ -217,10 +217,9 @@ int main(int argc, char **argv)
                                        if (!strcmp(name, "undefined"))
                                                continue;
 
-                                       fwrite(p_start, p - p_start, 1, stdout);
-                                       fputc('(', stdout);
-                                       fputs(name, stdout);
-                                       fputc(')', stdout);
+                                       fwrite(p_start, p - p_start + 1, 1,
+                                              stdout);
+                                       printf(" (%s)", name);
                                        p_start = p + 1;
                                }
                        }
index 8864a31cc14f4c57fe0115a0bfe73904fdb1ae12..a62c9f8d185aabb9a719397ce6daeeaa61f6ce58 100644 (file)
@@ -473,6 +473,8 @@ int main(int argc, char **argv)
        struct object_entry **list;
        int i;
 
+       setup_git_directory();
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
index 793fa08096e83e9a5504ca2bdc73b9b4d2a4dbbd..0a43278924edae9d6e3ecdf4bb100bea10faef97 100644 (file)
@@ -600,6 +600,8 @@ int main(int argc, char **argv)
        unsigned char *sha1;
        char buf[42]; /* 40 byte sha1 + \n + \0 */
 
+       setup_git_directory();
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if(!strcmp(arg, "--")) {
diff --git a/path.c b/path.c
index 4d889473a78a2c6fa38a0b54a48fe55bd83e9b02..334b2bd195f56ac6aaf9f9b8a36fe59115cd758c 100644 (file)
--- a/path.c
+++ b/path.c
@@ -131,75 +131,121 @@ int validate_symref(const char *path)
        return -1;
 }
 
-static char *current_dir(void)
+static char *user_path(char *buf, char *path, int sz)
 {
-       return getcwd(pathname, sizeof(pathname));
-}
-
-static int user_chdir(char *path)
-{
-       char *dir = path;
+       struct passwd *pw;
+       char *slash;
+       int len, baselen;
 
-       if(*dir == '~') {               /* user-relative path */
-               struct passwd *pw;
-               char *slash = strchr(dir, '/');
-
-               dir++;
-               /* '~/' and '~' (no slash) means users own home-dir */
-               if(!*dir || *dir == '/')
-                       pw = getpwuid(getuid());
-               else {
-                       if (slash) {
-                               *slash = '\0';
-                               pw = getpwnam(dir);
-                               *slash = '/';
-                       }
-                       else
-                               pw = getpwnam(dir);
+       if (!path || path[0] != '~')
+               return NULL;
+       path++;
+       slash = strchr(path, '/');
+       if (path[0] == '/' || !path[0]) {
+               pw = getpwuid(getuid());
+       }
+       else {
+               if (slash) {
+                       *slash = 0;
+                       pw = getpwnam(path);
+                       *slash = '/';
                }
-
-               /* make sure we got something back that we can chdir() to */
-               if(!pw || chdir(pw->pw_dir) < 0)
-                       return -1;
-
-               if(!slash || !slash[1]) /* no path following username */
-                       return 0;
-
-               dir = slash + 1;
+               else
+                       pw = getpwnam(path);
        }
-
-       /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */
-       if(chdir(dir) < 0)
-               return -1;
-
-       return 0;
+       if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
+               return NULL;
+       baselen = strlen(pw->pw_dir);
+       memcpy(buf, pw->pw_dir, baselen);
+       while ((1 < baselen) && (buf[baselen-1] == '/')) {
+               buf[baselen-1] = 0;
+               baselen--;
+       }
+       if (slash && slash[1]) {
+               len = strlen(slash);
+               if (sz <= baselen + len)
+                       return NULL;
+               memcpy(buf + baselen, slash, len + 1);
+       }
+       return buf;
 }
 
+/*
+ * First, one directory to try is determined by the following algorithm.
+ *
+ * (0) If "strict" is given, the path is used as given and no DWIM is
+ *     done. Otherwise:
+ * (1) "~/path" to mean path under the running user's home directory;
+ * (2) "~user/path" to mean path under named user's home directory;
+ * (3) "relative/path" to mean cwd relative directory; or
+ * (4) "/absolute/path" to mean absolute directory.
+ *
+ * Unless "strict" is given, we try access() for existence of "%s.git/.git",
+ * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
+ * what we try.
+ *
+ * Second, we try chdir() to that.  Upon failure, we return NULL.
+ *
+ * Then, we try if the current directory is a valid git repository.
+ * Upon failure, we return NULL.
+ *
+ * If all goes well, we return the directory we used to chdir() (but
+ * before ~user is expanded), avoiding getcwd() resolving symbolic
+ * links.  User relative paths are also returned as they are given,
+ * except DWIM suffixing.
+ */
 char *enter_repo(char *path, int strict)
 {
-       if(!path)
+       static char used_path[PATH_MAX];
+       static char validated_path[PATH_MAX];
+
+       if (!path)
                return NULL;
 
-       if (strict) {
-               if (chdir(path) < 0)
+       if (!strict) {
+               static const char *suffix[] = {
+                       ".git/.git", "/.git", ".git", "", NULL,
+               };
+               int len = strlen(path);
+               int i;
+               while ((1 < len) && (path[len-1] == '/')) {
+                       path[len-1] = 0;
+                       len--;
+               }
+               if (PATH_MAX <= len)
                        return NULL;
-       }
-       else {
-               if (!*path)
-                       ; /* happy -- no chdir */
-               else if (!user_chdir(path))
-                       ; /* happy -- as given */
-               else if (!user_chdir(mkpath("%s.git", path)))
-                       ; /* happy -- uemacs --> uemacs.git */
-               else
+               if (path[0] == '~') {
+                       if (!user_path(used_path, path, PATH_MAX))
+                               return NULL;
+                       strcpy(validated_path, path);
+                       path = used_path;
+               }
+               else if (PATH_MAX - 10 < len)
+                       return NULL;
+               else {
+                       path = strcpy(used_path, path);
+                       strcpy(validated_path, path);
+               }
+               len = strlen(path);
+               for (i = 0; suffix[i]; i++) {
+                       strcpy(path + len, suffix[i]);
+                       if (!access(path, F_OK)) {
+                               strcat(validated_path, suffix[i]);
+                               break;
+                       }
+               }
+               if (!suffix[i] || chdir(path))
                        return NULL;
-               (void)chdir(".git");
+               path = validated_path;
        }
+       else if (chdir(path))
+               return NULL;
 
-       if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
-          validate_symref("HEAD") == 0) {
+       if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
+           validate_symref("HEAD") == 0) {
                putenv("GIT_DIR=.");
-               return current_dir();
+               check_repository_format();
+               return path;
        }
 
        return NULL;
index ee49bf3b7b123359153a961c56f5aa3d58515864..a90cf2206925ff51b7e8c799b22c8e6cb09c7fc2 100644 (file)
@@ -27,6 +27,9 @@ int main(int argc, char **argv)
        char *dest = NULL;
        int fd[2];
        pid_t pid;
+       int nongit = 0;
+
+       setup_git_directory_gently(&nongit);
 
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
index 26123f7f6bb8bf9cc7fc27905fe2b67d66646f05..d24b097114ef2610416b106ea9aa2000873924f3 100644 (file)
@@ -58,6 +58,8 @@ int main(int argc, char **argv)
 {
        int i;
 
+       setup_git_directory();
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
diff --git a/quote.c b/quote.c
index e662a7da71e39c1ffc3d1a4bf485fda17df5e907..76eb14426581f03d0b15ee4eb720456a0729d1bc 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -112,7 +112,8 @@ char *sq_dequote(char *arg)
  *     but not enclosed in double-quote pair.  Return value is undefined.
  */
 
-int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+static int quote_c_style_counted(const char *name, int namelen,
+                                char *outbuf, FILE *outfp, int no_dq)
 {
 #undef EMIT
 #define EMIT(c) \
@@ -125,7 +126,7 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
 
        if (!no_dq)
                EMIT('"');
-       for (sp = name; (ch = *sp++); ) {
+       for (sp = name; (ch = *sp++) && (sp - name) <= namelen; ) {
 
                if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
                    (ch == 0177)) {
@@ -162,6 +163,12 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
        return needquote ? count : 0;
 }
 
+int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+{
+       int cnt = strlen(name);
+       return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
+}
+
 /*
  * C-style name unquoting.
  *
@@ -227,28 +234,30 @@ char *unquote_c_style(const char *quoted, const char **endp)
        }
 }
 
-void write_name_quoted(const char *prefix, const char *name,
-                      int quote, FILE *out)
+void write_name_quoted(const char *prefix, int prefix_len,
+                      const char *name, int quote, FILE *out)
 {
        int needquote;
 
        if (!quote) {
        no_quote:
-               if (prefix && prefix[0])
-                       fputs(prefix, out);
+               if (prefix_len)
+                       fprintf(out, "%.*s", prefix_len, prefix);
                fputs(name, out);
                return;
        }
 
        needquote = 0;
-       if (prefix && prefix[0])
-               needquote = quote_c_style(prefix, NULL, NULL, 0);
+       if (prefix_len)
+               needquote = quote_c_style_counted(prefix, prefix_len,
+                                                 NULL, NULL, 0);
        if (!needquote)
                needquote = quote_c_style(name, NULL, NULL, 0);
        if (needquote) {
                fputc('"', out);
-               if (prefix && prefix[0])
-                       quote_c_style(prefix, NULL, out, 1);
+               if (prefix_len)
+                       quote_c_style_counted(prefix, prefix_len,
+                                             NULL, out, 1);
                quote_c_style(name, NULL, out, 1);
                fputc('"', out);
        }
diff --git a/quote.h b/quote.h
index 2486e6e68c2d7fa7bc2f78c18df323cc9adcc894..c1ab3788e6d69318638142ebeffc690318a0489a 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -41,7 +41,7 @@ extern int quote_c_style(const char *name, char *outbuf, FILE *outfp,
                         int nodq);
 extern char *unquote_c_style(const char *quoted, const char **endp);
 
-extern void write_name_quoted(const char *prefix, const char *name,
-                             int quote, FILE *out);
+extern void write_name_quoted(const char *prefix, int prefix_len,
+                             const char *name, int quote, FILE *out);
 
 #endif
index df156ea0dad15ee810e8036442dfcab9202ef46a..e3b9c0d9fa1cb5a03d1a3a08f992074293285904 100644 (file)
@@ -629,6 +629,8 @@ int main(int argc, char **argv)
        unsigned char sha1[20];
        merge_fn_t fn = NULL;
 
+       setup_git_directory();
+
        newfd = hold_index_file_for_update(&cache_file, get_index_file());
        if (newfd < 0)
                die("unable to create new cachefile");
index e17f928061250c24465e1b96f89a18acb98a748c..8020d974f2fc84d477920bd10ea450c44fce0a77 100644 (file)
@@ -350,7 +350,8 @@ static int count_distance(struct commit_list *entry)
 
                if (commit->object.flags & (UNINTERESTING | COUNTED))
                        break;
-               nr++;
+               if (!paths || (commit->object.flags & TREECHANGE))
+                       nr++;
                commit->object.flags |= COUNTED;
                p = commit->parents;
                entry = p;
@@ -362,6 +363,7 @@ static int count_distance(struct commit_list *entry)
                        }
                }
        }
+
        return nr;
 }
 
@@ -382,15 +384,20 @@ static struct commit_list *find_bisection(struct commit_list *list)
        nr = 0;
        p = list;
        while (p) {
-               nr++;
+               if (!paths || (p->item->object.flags & TREECHANGE))
+                       nr++;
                p = p->next;
        }
        closest = 0;
        best = list;
 
-       p = list;
-       while (p) {
-               int distance = count_distance(p);
+       for (p = list; p; p = p->next) {
+               int distance;
+
+               if (paths && !(p->item->object.flags & TREECHANGE))
+                       continue;
+
+               distance = count_distance(p);
                clear_distance(list);
                if (nr - distance < distance)
                        distance = nr - distance;
@@ -398,7 +405,6 @@ static struct commit_list *find_bisection(struct commit_list *list)
                        best = p;
                        closest = distance;
                }
-               p = p->next;
        }
        if (best)
                best->next = NULL;
index 3eeb18f7c7d32412003ea665a47081dc06d84c95..2a14b00845c0211a0ddb38dad892ff2be0fdc8cc 100644 (file)
@@ -273,6 +273,7 @@ int main(int argc, char **argv)
        int fd[2], ret;
        pid_t pid;
 
+       setup_git_directory();
        argv++;
        for (i = 1; i < argc; i++, argv++) {
                char *arg = *argv;
diff --git a/setup.c b/setup.c
index ab3c778e805c166446d05c27617552da0f951f8c..d3556edf121ba78b0b83d20fdbd9cb397e79eb2b 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -47,6 +47,21 @@ const char *prefix_path(const char *prefix, int len, const char *path)
        return path;
 }
 
+/* 
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ */
+const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
+{
+       static char path[PATH_MAX];
+       if (!pfx || !*pfx || arg[0] == '/')
+               return arg;
+       memcpy(path, pfx, pfx_len);
+       strcpy(path + pfx_len, arg);
+       return path;
+}
+
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
        const char *entry = *pathspec;
@@ -92,7 +107,7 @@ static int is_toplevel_directory(void)
        return 1;
 }
 
-static const char *setup_git_directory_1(void)
+const char *setup_git_directory_gently(int *nongit_ok)
 {
        static char cwd[PATH_MAX+1];
        int len, offset;
@@ -116,7 +131,7 @@ static const char *setup_git_directory_1(void)
                if (validate_symref(path))
                        goto bad_dir_environ;
                if (getenv(DB_ENVIRONMENT)) {
-                       if (access(DB_ENVIRONMENT, X_OK))
+                       if (access(getenv(DB_ENVIRONMENT), X_OK))
                                goto bad_dir_environ;
                }
                else {
@@ -139,8 +154,15 @@ static const char *setup_git_directory_1(void)
                        break;
                chdir("..");
                do {
-                       if (!offset)
+                       if (!offset) {
+                               if (nongit_ok) {
+                                       if (chdir(cwd))
+                                               die("Cannot come back to cwd");
+                                       *nongit_ok = 1;
+                                       return NULL;
+                               }
                                die("Not a git repository");
+                       }
                } while (cwd[--offset] != '/');
        }
 
@@ -154,8 +176,25 @@ static const char *setup_git_directory_1(void)
        return cwd + offset;
 }
 
+int check_repository_format_version(const char *var, const char *value)
+{
+       if (strcmp(var, "core.repositoryformatversion") == 0)
+               repository_format_version = git_config_int(var, value);
+       return 0;
+}
+
+int check_repository_format(void)
+{
+       git_config(check_repository_format_version);
+       if (GIT_REPO_VERSION < repository_format_version)
+               die ("Expected git repo version <= %d, found %d",
+                    GIT_REPO_VERSION, repository_format_version);
+       return 0;
+}
+
 const char *setup_git_directory(void)
 {
-       const char *retval = setup_git_directory_1();
+       const char *retval = setup_git_directory_gently(NULL);
+       check_repository_format();
        return retval;
 }
diff --git a/shell.c b/shell.c
index 2c4789e944058c7dcab2deb5935a054691550d36..cd316185e96c73c1f7d0e6309abe3241c2deb7c4 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -5,8 +5,7 @@ static int do_generic_cmd(const char *me, char *arg)
 {
        const char *my_argv[4];
 
-       arg = sq_dequote(arg);
-       if (!arg)
+       if (!arg || !(arg = sq_dequote(arg)))
                die("bad argument");
 
        my_argv[0] = me;
index bf01fbc00d649231cb7ba49aba9432afe570b319..4eb9e04829b5925ea07c764239f91e801e158c78 100644 (file)
@@ -131,6 +131,8 @@ int main(int argc, char **argv)
        prog = getenv("GIT_SSH_PUSH");
        if (!prog) prog = "git-ssh-upload";
 
+       setup_git_directory();
+
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
                        get_tree = 1;
index 603abcc8c3da9029a50fb7a2e76b0d6f70e2abeb..b675a0b1f1df7d313f8201d59ad25b1c3d53a2f4 100644 (file)
@@ -121,6 +121,9 @@ int main(int argc, char **argv)
 
        prog = getenv(COUNTERPART_ENV_NAME);
        if (!prog) prog = COUNTERPART_PROGRAM_NAME;
+
+       setup_git_directory();
+
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 'w')
                        arg++;
index dff7d6916374d6f1c9302970cef7d0ab7e0c75ad..22bdacaf7861d15bb5fd383e9b17c22f082d72b7 100755 (executable)
@@ -126,19 +126,18 @@ test_expect_success \
     'git-ls-tree output for a known tree.' \
     'diff current expected'
 
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
 test_expect_success \
     'showing tree with git-ls-tree -r' \
     'git-ls-tree -r $tree >current'
 cat >expected <<\EOF
 100644 blob f87290f8eb2cbbea7857214459a0739927eab154   path0
 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01   path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe   path2
 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7   path2/file2
 120000 blob d8ce161addc5173867a3c3c730924388daedbc38   path2/file2sym
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3   path3
 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376   path3/file3
 120000 blob 8599103969b43aff7e430efea79ca4636466794f   path3/file3sym
-040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2   path3/subp3
 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f   path3/subp3/file3
 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c   path3/subp3/file3sym
 EOF
index c6ce56c86b95ae778b5e044999f8c5d8c0c70816..2ec06d3d3979aad18b4594329e93407d33f8cae5 100755 (executable)
@@ -54,6 +54,18 @@ test_expect_success \
      cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
+100644 blob X  path2/baz/b
+120000 blob X  path2/bazbo
+100644 blob X  path2/foo
+EOF
+     test_output'
+
+test_expect_success \
+    'ls-tree recursive with -t' \
+    'git-ls-tree -r -t $tree >current &&
+     cat >expected <<\EOF &&
+100644 blob X  path0
+120000 blob X  path1
 040000 tree X  path2
 040000 tree X  path2/baz
 100644 blob X  path2/baz/b
@@ -62,6 +74,15 @@ test_expect_success \
 EOF
      test_output'
 
+test_expect_success \
+    'ls-tree recursive with -d' \
+    'git-ls-tree -r -d $tree >current &&
+     cat >expected <<\EOF &&
+040000 tree X  path2
+040000 tree X  path2/baz
+EOF
+     test_output'
+
 test_expect_success \
     'ls-tree filtered with path' \
     'git-ls-tree $tree path >current &&
@@ -70,12 +91,14 @@ EOF
      test_output'
 
 
+# it used to be path1 and then path0, but with pathspec semantics
+# they are shown in canonical order.
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git-ls-tree $tree path1 path0 >current &&
      cat >expected <<\EOF &&
-120000 blob X  path1
 100644 blob X  path0
+120000 blob X  path1
 EOF
      test_output'
 
@@ -86,45 +109,49 @@ test_expect_success \
 EOF
      test_output'
 
+# It used to show path2 and its immediate children but
+# with pathspec semantics it shows only path2
 test_expect_success \
     'ls-tree filtered with path2' \
     'git-ls-tree $tree path2 >current &&
      cat >expected <<\EOF &&
 040000 tree X  path2
+EOF
+     test_output'
+
+# ... and path2/ shows the children.
+test_expect_success \
+    'ls-tree filtered with path2/' \
+    'git-ls-tree $tree path2/ >current &&
+     cat >expected <<\EOF &&
 040000 tree X  path2/baz
 120000 blob X  path2/bazbo
 100644 blob X  path2/foo
 EOF
      test_output'
 
+# The same change -- exact match does not show children of
+# path2/baz
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git-ls-tree $tree path2/baz >current &&
      cat >expected <<\EOF &&
 040000 tree X  path2/baz
-100644 blob X  path2/baz/b
 EOF
      test_output'
 
 test_expect_success \
-    'ls-tree filtered with path2' \
-    'git-ls-tree $tree path2 >current &&
+    'ls-tree filtered with path2/bak' \
+    'git-ls-tree $tree path2/bak >current &&
      cat >expected <<\EOF &&
-040000 tree X  path2
-040000 tree X  path2/baz
-120000 blob X  path2/bazbo
-100644 blob X  path2/foo
 EOF
      test_output'
 
 test_expect_success \
-    'ls-tree filtered with path2/' \
-    'git-ls-tree $tree path2/ >current &&
+    'ls-tree -t filtered with path2/bak' \
+    'git-ls-tree -t $tree path2/bak >current &&
      cat >expected <<\EOF &&
 040000 tree X  path2
-040000 tree X  path2/baz
-120000 blob X  path2/bazbo
-100644 blob X  path2/foo
 EOF
      test_output'
 
index 54103683480eb5f2a18ceac3f1018769ea31b18f..d78deb1e710056e236ba61f4b8ceb55e11c44cbf 100644 (file)
@@ -59,24 +59,16 @@ test_expect_success \
 EOF
      test_output'
 
+# Recursive does not show tree nodes anymore...
 test_expect_success \
     'ls-tree recursive' \
     'git-ls-tree -r $tree >current &&
      cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  2.txt
-040000 tree X  path0
-040000 tree X  path0/a
-040000 tree X  path0/a/b
-040000 tree X  path0/a/b/c
 100644 blob X  path0/a/b/c/1.txt
-040000 tree X  path1
-040000 tree X  path1/b
-040000 tree X  path1/b/c
 100644 blob X  path1/b/c/1.txt
-040000 tree X  path2
 100644 blob X  path2/1.txt
-040000 tree X  path3
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
@@ -110,41 +102,27 @@ test_expect_success \
 EOF
      test_output'
 
+# I am not so sure about this one after ls-tree doing pathspec match.
+# Having both path0/a and path0/a/b/c makes path0/a redundant, and
+# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
 test_expect_success \
     'ls-tree filter directories' \
     'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
      cat >expected <<\EOF &&
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path2
-100644 blob X  path2/1.txt
 040000 tree X  path0/a/b/c
-100644 blob X  path0/a/b/c/1.txt
 040000 tree X  path1/b/c
-100644 blob X  path1/b/c/1.txt
-040000 tree X  path0/a
-040000 tree X  path0/a/b
+040000 tree X  path2
+040000 tree X  path3
 EOF
      test_output'
 
+# Again, duplicates are filtered away so this is equivalent to
+# having 1.txt and path3
 test_expect_success \
     'ls-tree filter odd names' \
     'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
      cat >expected <<\EOF &&
 100644 blob X  1.txt
-100644 blob X  1.txt
-100644 blob X  1.txt
-100644 blob X  path3/1.txt
-100644 blob X  path3/1.txt
-100644 blob X  path3/1.txt
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path3
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
new file mode 100755 (executable)
index 0000000..a19d49d
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+test_description='Test merge with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "Hello" > init &&
+git add init &&
+git commit -m "Initial commit" &&
+git branch B &&
+mkdir dir &&
+echo "foo" > dir/foo &&
+git add dir/foo &&
+git commit -m "File: dir/foo" &&
+git checkout B &&
+echo "file dir" > dir &&
+git add dir &&
+git commit -m "File: dir"'
+
+test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
+test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
new file mode 100755 (executable)
index 0000000..e8606c7
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# nice decription of what this is about.
+
+
+test_description='Test criss-cross merge'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "1
+2
+3
+4
+5
+6
+7
+8
+9" > file &&
+git add file && 
+git commit -m "Initial commit" file &&
+git branch A &&
+git branch B &&
+git checkout A &&
+echo "1
+2
+3
+4
+5
+6
+7
+8 changed in B8, branch A
+9" > file &&
+git commit -m "B8" file &&
+git checkout B &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8
+9
+" > file &&
+git commit -m "C3" file &&
+git branch C3 &&
+git merge "pre E3 merge" B A &&
+echo "1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in B8, branch A
+9
+" > file &&
+git commit -m "E3" file &&
+git checkout A &&
+git merge "pre D8 merge" A C3 &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9" > file &&
+git commit -m D8 file'
+
+test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+
+cat > file-expect <<EOF
+1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9
+EOF
+
+test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
new file mode 100755 (executable)
index 0000000..43d74c5
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git-mv in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git-add path0/COPYING &&
+     git-commit -m add -a'
+
+test_expect_success \
+    'moving the file' \
+    'cd path0 && git-mv COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+    'commiting the change' \
+    'cd .. && git-commit -m move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+    grep -E "^R100.+path0/COPYING.+path1/COPYING"'
+
+test_done
index e654155a2eb0bc7aaf84335141d51c8f4fedb512..f2eccd79180deeb2283d60c00c12bc623f4e8317 100755 (executable)
@@ -133,6 +133,19 @@ test_expect_success () {
        fi
 }
 
+test_expect_code () {
+       test "$#" = 3 ||
+       error "bug in the test script: not 3 parameters to test-expect-code"
+       say >&3 "expecting exit code $1: $3"
+       test_run_ "$3"
+       if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+       then
+               test_ok_ "$2"
+       else
+               test_failure_ "$@"
+       fi
+}
+
 test_done () {
        trap - exit
        case "$test_failure" in
index 970c4bb54e148282a89f1249a96deca880bccafd..bacb23ae6365767dfdc93c6041aa9f3cb673b27b 100644 (file)
@@ -407,6 +407,8 @@ int main(int argc, char **argv)
        void *buffer;
        unsigned long size;
 
+       setup_git_directory();
+
        switch (argc) {
        case 3:
                basedir = argv[2];
diff --git a/tree.c b/tree.c
index 8b42a07b208aceeaa4900c034eea8a5b6b7ac323..043f032151cbca16dc144f9128b6327c755bcedd 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -9,9 +9,16 @@ const char *tree_type = "tree";
 
 static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-       int len = strlen(pathname);
-       unsigned int size = cache_entry_size(baselen + len);
-       struct cache_entry *ce = xmalloc(size);
+       int len;
+       unsigned int size;
+       struct cache_entry *ce;
+
+       if (S_ISDIR(mode))
+               return READ_TREE_RECURSIVE;
+
+       len = strlen(pathname);
+       size = cache_entry_size(baselen + len);
+       ce = xmalloc(size);
 
        memset(ce, 0, size);
 
@@ -67,9 +74,10 @@ static int match_tree_entry(const char *base, int baselen, const char *path, uns
        return 0;
 }
 
-static int read_tree_recursive(void *buffer, unsigned long size,
-                              const char *base, int baselen,
-                              int stage, const char **match)
+int read_tree_recursive(void *buffer, unsigned long size,
+                       const char *base, int baselen,
+                       int stage, const char **match,
+                       read_tree_fn_t fn)
 {
        while (size) {
                int len = strlen(buffer)+1;
@@ -86,6 +94,14 @@ static int read_tree_recursive(void *buffer, unsigned long size,
                if (!match_tree_entry(base, baselen, path, mode, match))
                        continue;
 
+               switch (fn(sha1, base, baselen, path, mode, stage)) {
+               case 0:
+                       continue;
+               case READ_TREE_RECURSIVE:
+                       break;;
+               default:
+                       return -1;
+               }
                if (S_ISDIR(mode)) {
                        int retval;
                        int pathlen = strlen(path);
@@ -106,22 +122,20 @@ static int read_tree_recursive(void *buffer, unsigned long size,
                        retval = read_tree_recursive(eltbuf, eltsize,
                                                     newbase,
                                                     baselen + pathlen + 1,
-                                                    stage, match);
+                                                    stage, match, fn);
                        free(eltbuf);
                        free(newbase);
                        if (retval)
                                return -1;
                        continue;
                }
-               if (read_one_entry(sha1, base, baselen, path, mode, stage) < 0)
-                       return -1;
        }
        return 0;
 }
 
 int read_tree(void *buffer, unsigned long size, int stage, const char **match)
 {
-       return read_tree_recursive(buffer, size, "", 0, stage, match);
+       return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry);
 }
 
 struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/tree.h b/tree.h
index 9975e88216dc6924c1d6e9ec3c7ae6d90c2df1dd..768e5e9eb894d37c0dca452bc7d96a1271035b55 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -35,4 +35,13 @@ int parse_tree(struct tree *tree);
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
 
+#define READ_TREE_RECURSIVE 1
+typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
+
+extern int read_tree_recursive(void *buffer, unsigned long size,
+                       const char *base, int baselen,
+                       int stage, const char **match,
+                       read_tree_fn_t fn);
+
+
 #endif /* TREE_H */
index d4ac3a5460568408f96fc26e5228c639dfc20439..07303f8bb3ef2b93f3e7c9bbe116aef9660584d5 100644 (file)
@@ -29,6 +29,8 @@ int main(int argc, char **argv)
        if (argc != 2 || get_sha1(argv[1], sha1))
                usage("git-unpack-file <sha1>");
 
+       setup_git_directory();
+
        puts(create_temp_file(sha1));
        return 0;
 }
index 8490895cf02df538327fa79bc0b2ffff2f06bd84..cfd61ae6b08c0635da4044ec3ab09f40c3178632 100644 (file)
@@ -269,6 +269,8 @@ int main(int argc, char **argv)
        int i;
        unsigned char sha1[20];
 
+       setup_git_directory();
+
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
 
index e824f62eaff8121095191f4710354aefb63eba56..0b6c3835bd19241ff447c37f442de75791186020 100644 (file)
@@ -19,5 +19,7 @@ int main(int ac, char **av)
        if (i != ac)
                usage(update_server_info_usage);
 
+       setup_git_directory();
+
        return !!update_server_info(force);
 }
index 2b2c6b77afd53870423d4bad9191ed669bc55660..0aac32f2271760f3f576ca2979e61fbd1ba83794 100644 (file)
@@ -86,9 +86,12 @@ static int write_tree(struct cache_entry **cachep, int maxentries, const char *b
 int main(int argc, char **argv)
 {
        int i, funny;
-       int entries = read_cache();
+       int entries;
        unsigned char sha1[20];
        
+       setup_git_directory();
+
+       entries = read_cache();
        if (argc == 2) {
                if (!strcmp(argv[1], "--missing-ok"))
                        missing_ok = 1;