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