GIT 0.99.9l aka 1.0rc4
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index a25d04a..200c9b2 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -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
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 39fa665..8a39970 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -8,13 +8,13 @@
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
-----------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index ab4dcae..9a7700f 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
-'git-cat-file' (-t | -s | <type>) <object>
+'git-cat-file' (-t | -s | -e | <type>) <object>
DESCRIPTION
-----------
@@ -29,6 +29,10 @@
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 @@
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.
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 3b04bfe..67f5126 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -21,6 +21,15 @@
-------
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
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index dc7d725..8890754 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
-'git-mailinfo' [-k] [-u] <msg> <patch>
+'git-mailinfo' [-k] [-u | --encoding=<encoding>] <msg> <patch>
DESCRIPTION
@@ -37,10 +37,17 @@
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.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 904e2fc..0cac563 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -37,6 +37,103 @@
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]
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index f2d5882..3013b8d 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -8,14 +8,14 @@
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 @@
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
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 8b91847..6e92e4a 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -28,11 +28,14 @@
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 @@
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.
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index fcc79fa..f8dbee7 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -10,7 +10,7 @@
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 @@
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.
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 95de436..841c9dc 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,26 @@
--------
'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 @@
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
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 6413d52..b5b9792 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -15,10 +15,10 @@
- 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 @@
`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]
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index e2dfb00..cf7ba76 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -898,9 +898,8 @@
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 @@
------------------------------------------------
$ 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 @@
------------------------------------------------
$ 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
------------------------------------------------
diff --git a/Makefile b/Makefile
index adff025..ba05986 100644
--- 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 @@
# 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 @@
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 @@
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 @@
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 @@
-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 @@
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
-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:
$(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 50be8f3..1742ab2 100644
--- 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 @@
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 @@
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 6ac94c5..f9b367f 100644
--- a/cache.h
+++ b/cache.h
@@ -147,8 +147,10 @@
#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 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 @@
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
diff --git a/cat-file.c b/cat-file.c
index d775a15..7594108 100644
--- a/cat-file.c
+++ b/cat-file.c
@@ -11,27 +11,44 @@
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>");
+ usage("git-cat-file [-t|-s|-e|<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;
- }
+ 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)
diff --git a/checkout-index.c b/checkout-index.c
index dab3778..f1e716d 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -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 @@
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 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 @@
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)
diff --git a/clone-pack.c b/clone-pack.c
index 9609219..a99a95c 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -271,6 +271,8 @@
int fd[2];
pid_t pid;
+ setup_git_directory();
+
nr_heads = 0;
heads = NULL;
for (i = 1; i < argc; i++) {
diff --git a/commit-tree.c b/commit-tree.c
index b60299f..4634b50 100644
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -91,6 +91,8 @@
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
index 0000000..94acd2d
--- /dev/null
+++ b/compat/setenv.c
@@ -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;
+}
diff --git a/config.c b/config.c
index 5cc8535..34584f6 100644
--- 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 @@
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 @@
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 @@
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.
*/
diff --git a/convert-objects.c b/convert-objects.c
index a892013..d78a8b4 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -316,6 +316,8 @@
unsigned char sha1[20];
struct entry *entry;
+ setup_git_directory();
+
if (argc != 2 || get_sha1(argv[1], sha1))
usage("git-convert-objects <sha1>");
diff --git a/daemon.c b/daemon.c
index 91b9656..539f6e8 100644
--- a/daemon.c
+++ b/daemon.c
@@ -82,9 +82,63 @@
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 @@
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);
diff --git a/debian/changelog b/debian/changelog
index 7356fe7..7e5d1ac 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -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.
diff --git a/diff-files.c b/diff-files.c
index 38599b5..6c0696c 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -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 @@
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 @@
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) {
diff --git a/environment.c b/environment.c
index b5026f1..0886ad3 100644
--- a/environment.c
+++ b/environment.c
@@ -13,6 +13,8 @@
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;
diff --git a/fetch-pack.c b/fetch-pack.c
index 6565982..58ba209 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -424,6 +424,8 @@
int fd[2];
pid_t pid;
+ setup_git_directory();
+
nr_heads = 0;
heads = NULL;
for (i = 1; i < argc; i++) {
diff --git a/fsck-objects.c b/fsck-objects.c
index 0433a1d..90e638e 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -431,6 +431,8 @@
{
int i, heads;
+ setup_git_directory();
+
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/git-add.sh b/git-add.sh
index b5fe46a..fdec86d 100755
--- a/git-add.sh
+++ b/git-add.sh
@@ -1,5 +1,10 @@
#!/bin/sh
+die () {
+ echo >&2 "$*"
+ exit 1
+}
+
usage() {
die "usage: git add [-n] [-v] <file>..."
}
diff --git a/git-applymbox.sh b/git-applymbox.sh
index 24d4a8c..c686cc8 100755
--- a/git-applymbox.sh
+++ b/git-applymbox.sh
@@ -33,7 +33,7 @@
-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
diff --git a/git-applypatch.sh b/git-applypatch.sh
index f0549960..4c577eb 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -120,26 +120,36 @@
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 @@
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) &&
diff --git a/git-bisect.sh b/git-bisect.sh
index d92993b..68838f3 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -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_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 @@
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 @@
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 @@
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_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 @@
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"
diff --git a/git-branch.sh b/git-branch.sh
index 4cd5da1..5306b27 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -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 @@
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 @@
fi
fi
git update-ref "refs/heads/$branchname" $rev
-
diff --git a/git-clone.sh b/git-clone.sh
index c09979a..699205e 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -73,7 +73,7 @@
*,-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)
diff --git a/git-count-objects.sh b/git-count-objects.sh
index d6e9a32..40c58ef 100755
--- a/git-count-objects.sh
+++ b/git-count-objects.sh
@@ -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
diff --git a/git-diff.sh b/git-diff.sh
index b3ec84b..b62e583 100755
--- a/git-diff.sh
+++ b/git-diff.sh
@@ -7,7 +7,10 @@
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 @@
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"
diff --git a/git-format-patch.sh b/git-format-patch.sh
index bc56876..1eebe85 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -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 @@
;;
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 @@
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 @@
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 @@
:
fi
else
- echo >&2 "* $file"
+ echo >&2 "$file"
process_one
fi
i=`expr "$i" + 1`
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 9dd7430..2beec2a 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -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
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index dc6a775..f699268 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -6,6 +6,11 @@
exit 1;
}
+die () {
+ echo >&2 "$*"
+ exit 1
+}
+
while case "$#" in 0) break;; esac
do
case "$1" in
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index bb58e22..7adffdc 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -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
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index c3eca8b..eafef77 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -26,7 +26,7 @@
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 @@
# 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 @@
;;
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"
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index 0129233..b7fb096 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -98,7 +98,7 @@
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 @@
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.")
diff --git a/git-merge.sh b/git-merge.sh
index d352a3c..a221daa 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -273,7 +273,8 @@
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.
diff --git a/git-mv.perl b/git-mv.perl
index bf54c38..b6c0b48 100755
--- a/git-mv.perl
+++ b/git-mv.perl
@@ -13,35 +13,15 @@
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;
diff --git a/git-rebase.sh b/git-rebase.sh
index 2bc3a12..638ff0d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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 @@
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 |
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 45d77c5..65868a9 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -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 @@
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 @@
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 @@
}
}
-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);
diff --git a/git-tag.sh b/git-tag.sh
index 16efc5b..2435a75 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -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=
diff --git a/git-verify-tag.sh b/git-verify-tag.sh
index 3c65f4a..1f44da5 100755
--- a/git-verify-tag.sh
+++ b/git-verify-tag.sh
@@ -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 @@
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 0b10b6e..619f25a 100644
--- a/git.c
+++ b/git.c
@@ -13,6 +13,10 @@
# 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 @@
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 3dd97e2..a847ef6 100755
--- a/gitk
+++ b/gitk
@@ -19,7 +19,7 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
.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 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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
-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 @@
} 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
$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 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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
$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 @@
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 @@
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 {
diff --git a/hash-object.c b/hash-object.c
index c8c9adb..ccba11c 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -29,6 +29,8 @@
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 @@
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;
}
diff --git a/http-fetch.c b/http-fetch.c
index 4353173..ad59f1c 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -922,6 +922,8 @@
int arg = 1;
int rc = 0;
+ setup_git_directory();
+
while (arg < argc && argv[arg][0] == '-') {
if (argv[arg][1] == 't') {
get_tree = 1;
diff --git a/http-push.c b/http-push.c
index 76c7886..fe92560 100644
--- a/http-push.c
+++ b/http-push.c
@@ -784,7 +784,7 @@
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 @@
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 rc = 0;
int i;
+ setup_git_directory();
setup_ident();
remote = xmalloc(sizeof(*remote));
@@ -1273,6 +1272,9 @@
break;
}
+ if (!remote->url)
+ usage(http_push_usage);
+
memset(remote_dir_exists, 0, 256);
http_init();
diff --git a/init-db.c b/init-db.c
index bd88291..8195b68 100644
--- a/init-db.c
+++ b/init-db.c
@@ -132,6 +132,23 @@
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 @@
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 @@
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 @@
if (create_symref(path, "refs/heads/master") < 0)
exit(1);
}
+
+ /* 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;
- copy_templates(path, len, template_path);
-
- /*
- * Find out if we can trust the executable bit.
- */
- safe_create_dir(path);
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 @@
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);
/*
diff --git a/local-fetch.c b/local-fetch.c
index 0931109..fa9e697 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -207,6 +207,8 @@
char *commit_id;
int arg = 1;
+ setup_git_directory();
+
while (arg < argc && argv[arg][0] == '-') {
if (argv[arg][1] == 't')
get_tree = 1;
diff --git a/ls-files.c b/ls-files.c
index db2288a..f3f1a6a 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -344,7 +344,7 @@
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 @@
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 @@
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);
}
}
diff --git a/ls-tree.c b/ls-tree.c
index d9f15e3..dae377d 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -11,217 +11,75 @@
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;
-
-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 int prepare_children(struct tree_entry_list *elem)
-{
- 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;
- }
- return 0;
-}
-
-static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
-{
- 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;
- }
- 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);
- }
- return err;
-}
-
-static int list_one(const char *path)
-{
- 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;
-}
+const char **pathspec;
static const char ls_tree_usage[] =
- "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
+ "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [path...]";
-int main(int argc, char **argv)
+static int show_recursive(const char *base, int baselen, const char *pathname)
{
- static char *path0[] = { "", NULL };
- char **path;
- unsigned char sha1[20];
+ 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;
+ }
+}
+
+static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+ 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;
+ }
+ type = "tree";
+ }
+ 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;
+}
+
+int main(int argc, const char **argv)
+{
+ 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 @@
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;
}
diff --git a/mailinfo.c b/mailinfo.c
index cb853df..890e348 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -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 @@
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 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);
+ 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) {
- fprintf(stderr, "cannot convert from %s to utf-8\n",
- 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 @@
}
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 @@
* 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 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]);
diff --git a/merge-base.c b/merge-base.c
index 751c3c2..e73fca7 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -236,6 +236,8 @@
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"))
diff --git a/merge-index.c b/merge-index.c
index 727527f..024196e 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -102,6 +102,7 @@
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 585677e..97e270a 100644
--- a/mktag.c
+++ b/mktag.c
@@ -111,6 +111,8 @@
if (argc != 1)
usage("cat <signaturefile> | git-mktag");
+ setup_git_directory();
+
// Read the signature
size = 0;
for (;;) {
diff --git a/name-rev.c b/name-rev.c
index 817e36b..65333d4 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -21,7 +21,7 @@
{
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 @@
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 @@
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;
}
}
diff --git a/pack-objects.c b/pack-objects.c
index 8864a31..a62c9f8 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -473,6 +473,8 @@
struct object_entry **list;
int i;
+ setup_git_directory();
+
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/pack-redundant.c b/pack-redundant.c
index 793fa08..0a43278 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -600,6 +600,8 @@
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 4d88947..334b2bd 100644
--- a/path.c
+++ b/path.c
@@ -131,75 +131,121 @@
return -1;
}
-static char *current_dir(void)
+static char *user_path(char *buf, char *path, int sz)
{
- return getcwd(pathname, sizeof(pathname));
-}
+ struct passwd *pw;
+ char *slash;
+ int len, baselen;
-static int user_chdir(char *path)
-{
- char *dir = path;
-
- 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);
- }
-
- /* 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;
- }
-
- /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */
- if(chdir(dir) < 0)
- return -1;
-
- return 0;
-}
-
-char *enter_repo(char *path, int strict)
-{
- if(!path)
+ if (!path || path[0] != '~')
return NULL;
-
- if (strict) {
- if (chdir(path) < 0)
- return NULL;
+ path++;
+ slash = strchr(path, '/');
+ if (path[0] == '/' || !path[0]) {
+ pw = getpwuid(getuid());
}
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 */
+ if (slash) {
+ *slash = 0;
+ pw = getpwnam(path);
+ *slash = '/';
+ }
else
- return NULL;
- (void)chdir(".git");
+ pw = getpwnam(path);
}
+ 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;
+}
- if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
- validate_symref("HEAD") == 0) {
+/*
+ * 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)
+{
+ static char used_path[PATH_MAX];
+ static char validated_path[PATH_MAX];
+
+ if (!path)
+ return NULL;
+
+ 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;
+ 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;
+ path = validated_path;
+ }
+ else if (chdir(path))
+ return NULL;
+
+ 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;
diff --git a/peek-remote.c b/peek-remote.c
index ee49bf3..a90cf22 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -27,6 +27,9 @@
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];
diff --git a/prune-packed.c b/prune-packed.c
index 26123f7..d24b097 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -58,6 +58,8 @@
{
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 e662a7d..76eb144 100644
--- a/quote.c
+++ b/quote.c
@@ -112,7 +112,8 @@
* 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 @@
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 @@
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 @@
}
}
-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 2486e6e..c1ab378 100644
--- a/quote.h
+++ b/quote.h
@@ -41,7 +41,7 @@
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
diff --git a/read-tree.c b/read-tree.c
index df156ea..e3b9c0d 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -629,6 +629,8 @@
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");
diff --git a/rev-list.c b/rev-list.c
index e17f928..8020d97 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -350,7 +350,8 @@
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 @@
}
}
}
+
return nr;
}
@@ -382,15 +384,20 @@
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 @@
best = p;
closest = distance;
}
- p = p->next;
}
if (best)
best->next = NULL;
diff --git a/send-pack.c b/send-pack.c
index 3eeb18f..2a14b00 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -273,6 +273,7 @@
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 ab3c778..d3556ed 100644
--- a/setup.c
+++ b/setup.c
@@ -47,6 +47,21 @@
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 @@
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 @@
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 @@
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 @@
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 2c4789e..cd31618 100644
--- a/shell.c
+++ b/shell.c
@@ -5,8 +5,7 @@
{
const char *my_argv[4];
- arg = sq_dequote(arg);
- if (!arg)
+ if (!arg || !(arg = sq_dequote(arg)))
die("bad argument");
my_argv[0] = me;
diff --git a/ssh-fetch.c b/ssh-fetch.c
index bf01fbc..4eb9e04 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -131,6 +131,8 @@
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;
diff --git a/ssh-upload.c b/ssh-upload.c
index 603abcc..b675a0b 100644
--- a/ssh-upload.c
+++ b/ssh-upload.c
@@ -121,6 +121,9 @@
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++;
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index dff7d69..22bdaca 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -126,19 +126,18 @@
'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
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index c6ce56c..2ec06d3 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -54,6 +54,18 @@
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
@@ -63,6 +75,15 @@
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 &&
cat >expected <<\EOF &&
@@ -70,12 +91,14 @@
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 @@
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'
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 5410368..d78deb1 100644
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -59,24 +59,16 @@
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 @@
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
index 0000000..a19d49d
--- /dev/null
+++ b/t/t6020-merge-df.sh
@@ -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
index 0000000..e8606c7
--- /dev/null
+++ b/t/t6021-merge-criss-cross.sh
@@ -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
index 0000000..43d74c5
--- /dev/null
+++ b/t/t7001-mv.sh
@@ -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
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e654155..f2eccd7 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -133,6 +133,19 @@
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
diff --git a/tar-tree.c b/tar-tree.c
index 970c4bb..bacb23a 100644
--- a/tar-tree.c
+++ b/tar-tree.c
@@ -407,6 +407,8 @@
void *buffer;
unsigned long size;
+ setup_git_directory();
+
switch (argc) {
case 3:
basedir = argv[2];
diff --git a/tree.c b/tree.c
index 8b42a07..043f032 100644
--- a/tree.c
+++ b/tree.c
@@ -9,9 +9,16 @@
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 @@
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 @@
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 @@
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 9975e88..768e5e9 100644
--- a/tree.h
+++ b/tree.h
@@ -35,4 +35,13 @@
/* 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 */
diff --git a/unpack-file.c b/unpack-file.c
index d4ac3a5..07303f8 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -29,6 +29,8 @@
if (argc != 2 || get_sha1(argv[1], sha1))
usage("git-unpack-file <sha1>");
+ setup_git_directory();
+
puts(create_temp_file(sha1));
return 0;
}
diff --git a/unpack-objects.c b/unpack-objects.c
index 8490895..cfd61ae 100644
--- a/unpack-objects.c
+++ b/unpack-objects.c
@@ -269,6 +269,8 @@
int i;
unsigned char sha1[20];
+ setup_git_directory();
+
for (i = 1 ; i < argc; i++) {
const char *arg = argv[i];
diff --git a/update-server-info.c b/update-server-info.c
index e824f62..0b6c383 100644
--- a/update-server-info.c
+++ b/update-server-info.c
@@ -19,5 +19,7 @@
if (i != ac)
usage(update_server_info_usage);
+ setup_git_directory();
+
return !!update_server_info(force);
}
diff --git a/write-tree.c b/write-tree.c
index 2b2c6b7..0aac32f 100644
--- a/write-tree.c
+++ b/write-tree.c
@@ -86,9 +86,12 @@
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;