diff --git a/Documentation/config.txt b/Documentation/config.txt index e7848055a9..798b551514 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -358,7 +358,8 @@ core.whitespace:: A comma separated list of common whitespace problems to notice. 'git-diff' will use `color.diff.whitespace` to highlight them, and 'git-apply --whitespace=error' will - consider them as errors: + consider them as errors. You can prefix `-` to disable + any of them (e.g. `-trailing-space`): + * `trailing-space` treats trailing whitespaces at the end of the line as an error (enabled by default). diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 2abfbdaadb..5aa69c0e12 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [[--track | --no-track] -b [-l]] [-m] [] -'git checkout' [] ... +'git checkout' [] [--] ... DESCRIPTION ----------- diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 91efac920e..26fd1b1117 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -87,8 +87,8 @@ then the cloned repository will become corrupt. --quiet:: -q:: - Operate quietly. This flag is passed to "rsync" and - 'git-fetch-pack' commands when given. + Operate quietly. This flag is also passed to the `rsync' + command when given. --no-checkout:: -n:: @@ -113,9 +113,8 @@ then the cloned repository will become corrupt. --upload-pack :: -u :: - When given, and the repository to clone from is handled - by 'git-fetch-pack', `--exec=` is passed to - the command to specify non-default path for the command + When given, and the repository to clone from is accessed + via ssh, this specifies a non-default path for the command run on the other end. --template=:: diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt index 0e45b58d83..8c8f35b7a7 100644 --- a/Documentation/git-diff-tree.txt +++ b/Documentation/git-diff-tree.txt @@ -93,11 +93,11 @@ include::pretty-options.txt[] This flag changes the way a merge commit patch is displayed, in a similar way to the '-c' option. It implies the '-c' and '-p' options and further compresses the patch output - by omitting hunks that show differences from only one - parent, or show the same change from all but one parent - for an Octopus merge. When this optimization makes all - hunks disappear, the commit itself and the commit log - message is not shown, just like in any other "empty diff" case. + by omitting uninteresting hunks whose the contents in the parents + have only two variants and the merge result picks one of them + without modification. When all hunks are uninteresting, the commit + itself and the commit log message is not shown, just like in any other + "empty diff" case. --always:: Show the commit itself and the commit log message even diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index a3edc00246..7ba9dab5e6 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -191,7 +191,7 @@ Thus you may instead want to use `rm -f filename` as the script. A significantly faster version: -------------------------------------------------------------------------- -git filter-branch --index-filter 'git update-index --remove filename' HEAD +git filter-branch --index-filter 'git rm --cached filename' HEAD -------------------------------------------------------------------------- Now, you will get the rewritten history saved in HEAD. diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt index beebd5301f..89f321b414 100644 --- a/Documentation/git-rerere.txt +++ b/Documentation/git-rerere.txt @@ -37,7 +37,7 @@ its working state. 'clear':: This resets the metadata used by rerere if a merge resolution is to be -is aborted. Calling 'git-am --skip' or 'git-rebase [--skip|--abort]' +aborted. Calling 'git-am [--skip|--abort]' or 'git-rebase [--skip|--abort]' will automatically invoke this command. 'diff':: diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index b8dc88fa38..046ab3542b 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -9,7 +9,8 @@ git-tag - Create, list, delete or verify a tag object signed with GPG SYNOPSIS -------- [verse] -'git tag' [-a | -s | -u ] [-f] [-m | -F ] [] +'git tag' [-a | -s | -u ] [-f] [-m | -F ] + [ | ] 'git tag' -d ... 'git tag' [-n[]] -l [] 'git tag' -v ... diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index b6f5d87e72..3aa38097e6 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -112,9 +112,9 @@ options may be given. See linkgit:git-diff-files[1] for more options. --cc:: This flag implies the '-c' options and further compresses the - patch output by omitting hunks that show differences from only - one parent, or show the same change from all but one parent for - an Octopus merge. + patch output by omitting uninteresting hunks whose contents in + the parents have only two variants and the merge result picks + one of them without modification. -r:: diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt index 92b3ecdae2..293bb15d20 100644 --- a/Documentation/technical/api-string-list.txt +++ b/Documentation/technical/api-string-list.txt @@ -41,7 +41,7 @@ memset(&list, 0, sizeof(struct string_list)); string_list_append("foo", &list); string_list_append("bar", &list); for (i = 0; i < list.nr; i++) - printf("%s\n", list.items[i].path) + printf("%s\n", list.items[i].string) ---- NOTE: It is more efficient to build an unsorted list and sort it @@ -113,7 +113,7 @@ Data structures * `struct string_list_item` -Represents an item of the list. The `path` member is a pointer to the +Represents an item of the list. The `string` member is a pointer to the string, and you may use the `util` member for any purpose, if you want. * `struct string_list` diff --git a/Makefile b/Makefile index b01cf1c993..798a2f2f77 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,16 @@ ALL_CFLAGS = $(CFLAGS) ALL_LDFLAGS = $(LDFLAGS) STRIP ?= strip +# Among the variables below, these: +# gitexecdir +# template_dir +# htmldir +# ETC_GITCONFIG (but not sysconfdir) +# can be specified as a relative path ../some/where/else (which must begin +# with ../); this is interpreted as relative to $(bindir) and "git" at +# runtime figures out where they are based on the path to the executable. +# This can help installing the suite in a relocatable way. + prefix = $(HOME) bindir = $(prefix)/bin mandir = $(prefix)/share/man @@ -205,7 +215,7 @@ GITWEB_FAVICON = git-favicon.png GITWEB_SITE_HEADER = GITWEB_SITE_FOOTER = -export prefix bindir gitexecdir sharedir htmldir sysconfdir +export prefix bindir sharedir htmldir sysconfdir CC = gcc AR = ar @@ -283,7 +293,6 @@ PROGRAMS += git-pack-redundant$X PROGRAMS += git-patch-id$X PROGRAMS += git-receive-pack$X PROGRAMS += git-send-pack$X -PROGRAMS += git-shell$X PROGRAMS += git-show-index$X PROGRAMS += git-unpack-file$X PROGRAMS += git-update-server-info$X @@ -410,6 +419,7 @@ LIB_OBJS += diff-no-index.o LIB_OBJS += diff-lib.o LIB_OBJS += diff.o LIB_OBJS += dir.o +LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o LIB_OBJS += exec_cmd.o @@ -745,6 +755,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o EXTLIBS += -lws2_32 X = .exe + gitexecdir = ../libexec/git-core template_dir = ../share/git-core/templates/ ETC_GITCONFIG = ../etc/gitconfig endif @@ -811,6 +822,7 @@ EXTLIBS += -lz ifndef NO_POSIX_ONLY_PROGRAMS PROGRAMS += git-daemon$X PROGRAMS += git-imap-send$X + PROGRAMS += git-shell$X endif ifndef NO_OPENSSL OPENSSL_LIBSSL = -lssl @@ -1255,8 +1267,12 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS echo "$$FLAGS" >GIT-CFLAGS; \ fi +# We need to apply sq twice, once to protect from the shell +# that runs GIT-BUILD-OPTIONS, and then again to protect it +# and the first level quoting from the shell that runs "echo". GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS - @echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@ + @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ + @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK @@ -1310,35 +1326,44 @@ remove-dashes: ### Installation rules ifeq ($(firstword $(subst /, ,$(template_dir))),..) -template_instdir = $(gitexecdir)/$(template_dir) +template_instdir = $(bindir)/$(template_dir) else template_instdir = $(template_dir) endif export template_instdir +ifeq ($(firstword $(subst /, ,$(gitexecdir))),..) +gitexec_instdir = $(bindir)/$(gitexecdir) +else +gitexec_instdir = $(gitexecdir) +endif +gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir)) +export gitexec_instdir + install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' - $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK $(MAKE) -C gitk-git install - $(MAKE) -C git-gui install + $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install endif - if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \ - then \ - ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ - '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' || \ - cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ - '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ - fi - $(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) ifneq (,$X) - $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';) endif - ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X' + bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ + execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ + if test "z$$bindir" != "z$$execdir"; \ + then \ + ln -f "$$bindir/git$X" "$$execdir/git$X" || \ + cp "$$bindir/git$X" "$$execdir/git$X"; \ + fi && \ + { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ + $(RM) "$$execdir/git$X" && \ + ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: $(MAKE) -C Documentation install diff --git a/abspath.c b/abspath.c index 4f95a954d5..0d561246e0 100644 --- a/abspath.c +++ b/abspath.c @@ -66,3 +66,39 @@ const char *make_absolute_path(const char *path) return buf; } + +static const char *get_pwd_cwd(void) +{ + static char cwd[PATH_MAX + 1]; + char *pwd; + struct stat cwd_stat, pwd_stat; + if (getcwd(cwd, PATH_MAX) == NULL) + return NULL; + pwd = getenv("PWD"); + if (pwd && strcmp(pwd, cwd)) { + stat(cwd, &cwd_stat); + if (!stat(pwd, &pwd_stat) && + pwd_stat.st_dev == cwd_stat.st_dev && + pwd_stat.st_ino == cwd_stat.st_ino) { + strlcpy(cwd, pwd, PATH_MAX); + } + } + return cwd; +} + +const char *make_nonrelative_path(const char *path) +{ + static char buf[PATH_MAX + 1]; + + if (is_absolute_path(path)) { + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } else { + const char *cwd = get_pwd_cwd(); + if (!cwd) + die("Cannot determine the current working directory"); + if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } + return buf; +} diff --git a/archive.c b/archive.c index b8b45bad77..f834b5f51f 100644 --- a/archive.c +++ b/archive.c @@ -1,8 +1,23 @@ #include "cache.h" #include "commit.h" +#include "tree-walk.h" #include "attr.h" #include "archive.h" +static const char archive_usage[] = \ +"git archive --format= [--prefix=/] [--verbose] [] [path...]"; + +#define USES_ZLIB_COMPRESSION 1 + +const struct archiver { + const char *name; + write_archive_fn_t write_archive; + unsigned int flags; +} archivers[] = { + { "tar", write_tar_archive }, + { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, +}; + static void format_subst(const struct commit *commit, const char *src, size_t len, struct strbuf *buf) @@ -155,3 +170,147 @@ int write_archive_entries(struct archiver_args *args, err = 0; return err; } + +static const struct archiver *lookup_archiver(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(archivers); i++) { + if (!strcmp(name, archivers[i].name)) + return &archivers[i]; + } + return NULL; +} + +static void parse_pathspec_arg(const char **pathspec, + struct archiver_args *ar_args) +{ + ar_args->pathspec = get_pathspec(ar_args->base, pathspec); +} + +static void parse_treeish_arg(const char **argv, + struct archiver_args *ar_args, const char *prefix) +{ + const char *name = argv[0]; + const unsigned char *commit_sha1; + time_t archive_time; + struct tree *tree; + const struct commit *commit; + unsigned char sha1[20]; + + if (get_sha1(name, sha1)) + die("Not a valid object name"); + + commit = lookup_commit_reference_gently(sha1, 1); + if (commit) { + commit_sha1 = commit->object.sha1; + archive_time = commit->date; + } else { + commit_sha1 = NULL; + archive_time = time(NULL); + } + + tree = parse_tree_indirect(sha1); + if (tree == NULL) + die("not a tree object"); + + if (prefix) { + unsigned char tree_sha1[20]; + unsigned int mode; + int err; + + err = get_tree_entry(tree->object.sha1, prefix, + tree_sha1, &mode); + if (err || !S_ISDIR(mode)) + die("current working directory is untracked"); + + tree = parse_tree_indirect(tree_sha1); + } + ar_args->tree = tree; + ar_args->commit_sha1 = commit_sha1; + ar_args->commit = commit; + ar_args->time = archive_time; +} + +static int parse_archive_args(int argc, const char **argv, + const struct archiver **ar, struct archiver_args *args) +{ + const char *format = "tar"; + const char *base = ""; + int compression_level = -1; + int verbose = 0; + int i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); + } + if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose = 1; + continue; + } + if (!prefixcmp(arg, "--format=")) { + format = arg + 9; + continue; + } + if (!prefixcmp(arg, "--prefix=")) { + base = arg + 9; + continue; + } + if (!strcmp(arg, "--")) { + i++; + break; + } + if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { + compression_level = arg[1] - '0'; + continue; + } + if (arg[0] == '-') + die("Unknown argument: %s", arg); + break; + } + + /* We need at least one parameter -- tree-ish */ + if (argc - 1 < i) + usage(archive_usage); + *ar = lookup_archiver(format); + if (!*ar) + die("Unknown archive format '%s'", format); + + args->compression_level = Z_DEFAULT_COMPRESSION; + if (compression_level != -1) { + if ((*ar)->flags & USES_ZLIB_COMPRESSION) + args->compression_level = compression_level; + else { + die("Argument not supported for format '%s': -%d", + format, compression_level); + } + } + args->verbose = verbose; + args->base = base; + args->baselen = strlen(base); + + return i; +} + +int write_archive(int argc, const char **argv, const char *prefix, + int setup_prefix) +{ + const struct archiver *ar = NULL; + struct archiver_args args; + int tree_idx; + + tree_idx = parse_archive_args(argc, argv, &ar, &args); + if (setup_prefix && prefix == NULL) + prefix = setup_git_directory(); + + argv += tree_idx; + parse_treeish_arg(argv, &args, prefix); + parse_pathspec_arg(argv + 1, &args); + + return ar->write_archive(&args); +} diff --git a/archive.h b/archive.h index 4a02371f37..0b15b35143 100644 --- a/archive.h +++ b/archive.h @@ -1,9 +1,6 @@ #ifndef ARCHIVE_H #define ARCHIVE_H -#define MAX_EXTRA_ARGS 32 -#define MAX_ARGS (MAX_EXTRA_ARGS + 32) - struct archiver_args { const char *base; size_t baselen; @@ -20,20 +17,6 @@ typedef int (*write_archive_fn_t)(struct archiver_args *); typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size); -struct archiver { - const char *name; - write_archive_fn_t write_archive; - unsigned int flags; -}; - -extern int parse_archive_args(int argc, const char **argv, const struct archiver **ar, struct archiver_args *args); - -extern void parse_treeish_arg(const char **treeish, - struct archiver_args *ar_args, - const char *prefix); - -extern void parse_pathspec_arg(const char **pathspec, - struct archiver_args *args); /* * Archive-format specific backends. */ @@ -41,5 +24,6 @@ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry); +extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index df97724696..22445acbfc 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,21 +5,9 @@ #include "cache.h" #include "builtin.h" #include "archive.h" -#include "commit.h" -#include "tree-walk.h" #include "pkt-line.h" #include "sideband.h" -static const char archive_usage[] = \ -"git archive --format= [--prefix=/] [--verbose] [] [path...]"; - -#define USES_ZLIB_COMPRESSION 1 - -const struct archiver archivers[] = { - { "tar", write_tar_archive }, - { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, -}; - static int run_remote_archiver(const char *remote, int argc, const char **argv) { @@ -27,7 +15,7 @@ static int run_remote_archiver(const char *remote, int argc, int fd[2], i, len, rv; struct child_process *conn; const char *exec = "git-upload-archive"; - int exec_at = 0; + int exec_at = 0, exec_value_at = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -36,7 +24,14 @@ static int run_remote_archiver(const char *remote, int argc, die("multiple --exec specified"); exec = arg + 7; exec_at = i; - break; + } else if (!strcmp(arg, "--exec")) { + if (exec_at) + die("multiple --exec specified"); + if (i + 1 >= argc) + die("option --exec requires a value"); + exec = argv[i + 1]; + exec_at = i; + exec_value_at = ++i; } } @@ -44,7 +39,7 @@ static int run_remote_archiver(const char *remote, int argc, conn = git_connect(fd, url, exec, 0); for (i = 1; i < argc; i++) { - if (i == exec_at) + if (i == exec_at || i == exec_value_at) continue; packet_write(fd[1], "argument %s\n", argv[i]); } @@ -74,131 +69,6 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static const struct archiver *lookup_archiver(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(archivers); i++) { - if (!strcmp(name, archivers[i].name)) - return &archivers[i]; - } - return NULL; -} - -void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) -{ - ar_args->pathspec = get_pathspec(ar_args->base, pathspec); -} - -void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, - const char *prefix) -{ - const char *name = argv[0]; - const unsigned char *commit_sha1; - time_t archive_time; - struct tree *tree; - const struct commit *commit; - unsigned char sha1[20]; - - if (get_sha1(name, sha1)) - die("Not a valid object name"); - - commit = lookup_commit_reference_gently(sha1, 1); - if (commit) { - commit_sha1 = commit->object.sha1; - archive_time = commit->date; - } else { - commit_sha1 = NULL; - archive_time = time(NULL); - } - - tree = parse_tree_indirect(sha1); - if (tree == NULL) - die("not a tree object"); - - if (prefix) { - unsigned char tree_sha1[20]; - unsigned int mode; - int err; - - err = get_tree_entry(tree->object.sha1, prefix, - tree_sha1, &mode); - if (err || !S_ISDIR(mode)) - die("current working directory is untracked"); - - tree = parse_tree_indirect(tree_sha1); - } - ar_args->tree = tree; - ar_args->commit_sha1 = commit_sha1; - ar_args->commit = commit; - ar_args->time = archive_time; -} - -int parse_archive_args(int argc, const char **argv, const struct archiver **ar, - struct archiver_args *args) -{ - const char *format = "tar"; - const char *base = ""; - int compression_level = -1; - int verbose = 0; - int i; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - for (i = 0; i < ARRAY_SIZE(archivers); i++) - printf("%s\n", archivers[i].name); - exit(0); - } - if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!prefixcmp(arg, "--format=")) { - format = arg + 9; - continue; - } - if (!prefixcmp(arg, "--prefix=")) { - base = arg + 9; - continue; - } - if (!strcmp(arg, "--")) { - i++; - break; - } - if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { - compression_level = arg[1] - '0'; - continue; - } - if (arg[0] == '-') - die("Unknown argument: %s", arg); - break; - } - - /* We need at least one parameter -- tree-ish */ - if (argc - 1 < i) - usage(archive_usage); - *ar = lookup_archiver(format); - if (!*ar) - die("Unknown archive format '%s'", format); - - args->compression_level = Z_DEFAULT_COMPRESSION; - if (compression_level != -1) { - if ((*ar)->flags & USES_ZLIB_COMPRESSION) - args->compression_level = compression_level; - else { - die("Argument not supported for format '%s': -%d", - format, compression_level); - } - } - args->verbose = verbose; - args->base = base; - args->baselen = strlen(base); - - return i; -} - static const char *extract_remote_arg(int *ac, const char **av) { int ix, iy, cnt = *ac; @@ -215,6 +85,13 @@ static const char *extract_remote_arg(int *ac, const char **av) die("Multiple --remote specified"); remote = arg + 9; continue; + } else if (!strcmp(arg, "--remote")) { + if (remote) + die("Multiple --remote specified"); + if (++ix >= cnt) + die("option --remote requires a value"); + remote = av[ix]; + continue; } if (arg[0] != '-') no_more_options = 1; @@ -232,9 +109,6 @@ static const char *extract_remote_arg(int *ac, const char **av) int cmd_archive(int argc, const char **argv, const char *prefix) { - const struct archiver *ar = NULL; - struct archiver_args args; - int tree_idx; const char *remote = NULL; remote = extract_remote_arg(&argc, argv); @@ -243,13 +117,5 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - tree_idx = parse_archive_args(argc, argv, &ar, &args); - if (prefix == NULL) - prefix = setup_git_directory(); - - argv += tree_idx; - parse_treeish_arg(argv, &args, prefix); - parse_pathspec_arg(argv + 1, &args); - - return ar->write_archive(&args); + return write_archive(argc, argv, prefix, 1); } diff --git a/builtin-branch.c b/builtin-branch.c index b885bd132b..5db8ad836a 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -13,6 +13,8 @@ #include "remote.h" #include "parse-options.h" #include "branch.h" +#include "diff.h" +#include "revision.h" static const char * const builtin_branch_usage[] = { "git branch [options] [-r | -a] [--merged | --no-merged]", @@ -22,10 +24,8 @@ static const char * const builtin_branch_usage[] = { NULL }; -#define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 #define REF_REMOTE_BRANCH 0x02 -#define REF_TAG 0x04 static const char *head; static unsigned char head_sha1[20]; @@ -181,25 +181,21 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) struct ref_item { char *name; unsigned int kind; - unsigned char sha1[20]; + struct commit *commit; }; struct ref_list { + struct rev_info revs; int index, alloc, maxwidth; struct ref_item *list; struct commit_list *with_commit; int kinds; }; -static int has_commit(const unsigned char *sha1, struct commit_list *with_commit) +static int has_commit(struct commit *commit, struct commit_list *with_commit) { - struct commit *commit; - if (!with_commit) return 1; - commit = lookup_commit_reference_gently(sha1, 1); - if (!commit) - return 0; while (with_commit) { struct commit *other; @@ -215,7 +211,8 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, { struct ref_list *ref_list = (struct ref_list*)(cb_data); struct ref_item *newitem; - int kind = REF_UNKNOWN_TYPE; + struct commit *commit; + int kind; int len; static struct commit_list branch; @@ -226,13 +223,15 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, } else if (!prefixcmp(refname, "refs/remotes/")) { kind = REF_REMOTE_BRANCH; refname += 13; - } else if (!prefixcmp(refname, "refs/tags/")) { - kind = REF_TAG; - refname += 10; - } + } else + return 0; + + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + return error("branch '%s' does not point at a commit", refname); /* Filter with with_commit if specified */ - if (!has_commit(sha1, ref_list->with_commit)) + if (!has_commit(commit, ref_list->with_commit)) return 0; /* Don't add types the caller doesn't want */ @@ -243,12 +242,8 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, branch.item = lookup_commit_reference_gently(sha1, 1); if (!branch.item) die("Unable to lookup tip of branch %s", refname); - if (merge_filter == SHOW_NOT_MERGED && - has_commit(merge_filter_ref, &branch)) - return 0; - if (merge_filter == SHOW_MERGED && - !has_commit(merge_filter_ref, &branch)) - return 0; + add_pending_object(&ref_list->revs, + (struct object *)branch.item, refname); } /* Resize buffer */ @@ -262,7 +257,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, newitem = &(ref_list->list[ref_list->index++]); newitem->name = xstrdup(refname); newitem->kind = kind; - hashcpy(newitem->sha1, sha1); + newitem->commit = commit; len = strlen(newitem->name); if (len > ref_list->maxwidth) ref_list->maxwidth = len; @@ -309,7 +304,13 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, { char c; int color; - struct commit *commit; + struct commit *commit = item->commit; + + if (merge_filter != NO_FILTER) { + int is_merged = !!(item->commit->object.flags & UNINTERESTING); + if (is_merged != (merge_filter == SHOW_MERGED)) + return; + } switch (item->kind) { case REF_LOCAL_BRANCH: @@ -337,7 +338,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, strbuf_init(&subject, 0); stat[0] = '\0'; - commit = lookup_commit(item->sha1); + commit = item->commit; if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, &subject, 0, NULL, NULL, 0, 0); @@ -350,7 +351,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), - find_unique_abbrev(item->sha1, abbrev), + find_unique_abbrev(item->commit->object.sha1, abbrev), stat, sub); strbuf_release(&subject); } else { @@ -363,22 +364,34 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str { int i; struct ref_list ref_list; + struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); memset(&ref_list, 0, sizeof(ref_list)); ref_list.kinds = kinds; ref_list.with_commit = with_commit; + if (merge_filter != NO_FILTER) + init_revisions(&ref_list.revs, NULL); for_each_ref(append_ref, &ref_list); + if (merge_filter != NO_FILTER) { + struct commit *filter; + filter = lookup_commit_reference_gently(merge_filter_ref, 0); + filter->object.flags |= UNINTERESTING; + add_pending_object(&ref_list.revs, + (struct object *) filter, ""); + ref_list.revs.limited = 1; + prepare_revision_walk(&ref_list.revs); + } qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached && has_commit(head_sha1, with_commit)) { + if (detached && head_commit && has_commit(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); item.kind = REF_LOCAL_BRANCH; - hashcpy(item.sha1, head_sha1); + item.commit = head_commit; if (strlen(item.name) > ref_list.maxwidth) - ref_list.maxwidth = strlen(item.name); + ref_list.maxwidth = strlen(item.name); print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); free(item.name); } diff --git a/builtin-checkout.c b/builtin-checkout.c index fbd5105a83..411cc513c6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -430,6 +430,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_END(), }; + int has_dash_dash; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); @@ -438,12 +439,57 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track = git_branch_track; - argc = parse_options(argc, argv, options, checkout_usage, 0); + argc = parse_options(argc, argv, options, checkout_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (!opts.new_branch && (opts.track != git_branch_track)) + die("git checkout: --track and --no-track require -b"); + + if (opts.force && opts.merge) + die("git checkout: -f and -m are incompatible"); + + /* + * case 1: git checkout -- [] + * + * must be a valid tree, everything after the '--' must be + * a path. + * + * case 2: git checkout -- [] + * + * everything after the '--' must be paths. + * + * case 3: git checkout [] + * + * With no paths, if is a commit, that is to + * switch to the branch or detach HEAD at it. + * + * Otherwise shall not be ambiguous. + * - If it's *only* a reference, treat it like case (1). + * - If it's only a path, treat it like case (2). + * - else: fail. + * + */ if (argc) { + if (!strcmp(argv[0], "--")) { /* case (2) */ + argv++; + argc--; + goto no_reference; + } + arg = argv[0]; - if (get_sha1(arg, rev)) - ; - else if ((new.commit = lookup_commit_reference_gently(rev, 1))) { + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + + if (get_sha1(arg, rev)) { + if (has_dash_dash) /* case (1) */ + die("invalid reference: %s", arg); + goto no_reference; /* case (3 -> 2) */ + } + + /* we can't end up being in (2) anymore, eat the argument */ + argv++; + argc--; + + if ((new.commit = lookup_commit_reference_gently(rev, 1))) { new.name = arg; setup_branch_path(&new); if (resolve_ref(new.path, rev, 1, NULL)) @@ -452,25 +498,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) new.path = NULL; parse_commit(new.commit); source_tree = new.commit->tree; - argv++; - argc--; - } else if ((source_tree = parse_tree_indirect(rev))) { + } else + source_tree = parse_tree_indirect(rev); + + if (!source_tree) /* case (1): want a tree */ + die("reference is not a tree: %s", arg); + if (!has_dash_dash) {/* case (3 -> 1) */ + /* + * Do not complain the most common case + * git checkout branch + * even if there happen to be a file called 'branch'; + * it would be extremely annoying. + */ + if (argc) + verify_non_filename(NULL, arg); + } + else { argv++; argc--; } } - if (argc && !strcmp(argv[0], "--")) { - argv++; - argc--; - } - - if (!opts.new_branch && (opts.track != git_branch_track)) - die("git checkout: --track and --no-track require -b"); - - if (opts.force && opts.merge) - die("git checkout: -f and -m are incompatible"); - +no_reference: if (argc) { const char **pathspec = get_pathspec(prefix, argv); diff --git a/builtin-clone.c b/builtin-clone.c index 352224591f..e086a40b41 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -480,6 +480,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_quiet) transport->verbose = -1; + if (option_upload_pack) + transport_set_option(transport, TRANS_OPT_UPLOADPACK, + option_upload_pack); + refs = transport_get_remote_refs(transport); transport_fetch_refs(transport, refs); } diff --git a/builtin-commit.c b/builtin-commit.c index 97e64de312..9a11ca0bcd 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -68,8 +68,8 @@ static enum { static char *cleanup_arg; static int use_editor = 1, initial_commit, in_merge; -const char *only_include_assumed; -struct strbuf message; +static const char *only_include_assumed; +static struct strbuf message; static int opt_parse_m(const struct option *opt, const char *arg, int unset) { @@ -78,8 +78,7 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset) strbuf_setlen(buf, 0); else { strbuf_addstr(buf, arg); - strbuf_addch(buf, '\n'); - strbuf_addch(buf, '\n'); + strbuf_addstr(buf, "\n\n"); } return 0; } @@ -647,7 +646,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix) char index[PATH_MAX]; const char *env[2] = { index, NULL }; snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - launch_editor(git_path(commit_editmsg), NULL, env); + if (launch_editor(git_path(commit_editmsg), NULL, env)) { + fprintf(stderr, + "Please supply the message using either -m or -F option.\n"); + exit(1); + } } if (!no_verify && @@ -877,7 +880,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) } } -int git_commit_config(const char *k, const char *v, void *cb) +static int git_commit_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "commit.template")) return git_config_string(&template_file, k, v); diff --git a/builtin-config.c b/builtin-config.c index 0cf191a112..91fdc4985d 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -145,7 +145,7 @@ free_strings: return ret; } -char *normalize_value(const char *key, const char *value) +static char *normalize_value(const char *key, const char *value) { char *normalized; diff --git a/builtin-diff.c b/builtin-diff.c index faaa85a1d4..7ffea97505 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -296,7 +296,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ - if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) + if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) && + check_pager_config("diff") != 0) setup_pager(); /* diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 76282ad791..445039e19c 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -809,7 +809,7 @@ static struct ref_sort *default_sort(void) return sort; } -int opt_parse_sort(const struct option *opt, const char *arg, int unset) +static int opt_parse_sort(const struct option *opt, const char *arg, int unset) { struct ref_sort **sort_tail = opt->value; struct ref_sort *s; diff --git a/builtin-fsck.c b/builtin-fsck.c index 7326dc33a5..7a4a4f144f 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -377,6 +377,10 @@ static void fsck_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; + case 14: + if (prefixcmp(de->d_name, "tmp_obj_")) + break; + continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); diff --git a/builtin-merge.c b/builtin-merge.c index 8825dcf8d9..e78fa18b3a 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -431,7 +431,7 @@ static void merge_name(const char *remote, struct strbuf *msg) sha1_to_hex(remote_head->sha1), remote); } -int git_merge_config(const char *k, const char *v, void *cb) +static int git_merge_config(const char *k, const char *v, void *cb) { if (branch && !prefixcmp(k, "branch.") && !prefixcmp(k + 7, branch) && diff --git a/builtin-prune.c b/builtin-prune.c index 7de4cabe07..947de8cf25 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -13,6 +13,22 @@ static const char * const prune_usage[] = { static int show_only; static unsigned long expire; +static int prune_tmp_object(char *path, const char *filename) +{ + const char *fullpath = mkpath("%s/%s", path, filename); + if (expire) { + struct stat st; + if (lstat(fullpath, &st)) + return error("Could not stat '%s'", fullpath); + if (st.st_mtime > expire) + return 0; + } + printf("Removing stale temporary file %s\n", fullpath); + if (!show_only) + unlink(fullpath); + return 0; +} + static int prune_object(char *path, const char *filename, const unsigned char *sha1) { const char *fullpath = mkpath("%s/%s", path, filename); @@ -53,6 +69,11 @@ static int prune_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; + case 14: + if (prefixcmp(de->d_name, "tmp_obj_")) + break; + prune_tmp_object(path, de->d_name); + continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); @@ -105,23 +126,9 @@ static void remove_temporary_files(void) dirname); return; } - while ((de = readdir(dir)) != NULL) { - if (!prefixcmp(de->d_name, "tmp_")) { - char name[PATH_MAX]; - int c = snprintf(name, PATH_MAX, "%s/%s", - dirname, de->d_name); - if (c < 0 || c >= PATH_MAX) - continue; - if (expire) { - struct stat st; - if (stat(name, &st) != 0 || st.st_mtime >= expire) - continue; - } - printf("Removing stale temporary file %s\n", name); - if (!show_only) - unlink(name); - } - } + while ((de = readdir(dir)) != NULL) + if (!prefixcmp(de->d_name, "tmp_")) + prune_tmp_object(dirname, de->d_name); closedir(dir); } diff --git a/builtin-reset.c b/builtin-reset.c index 4d246c31b1..c24c219091 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -85,7 +85,7 @@ static void print_new_head_line(struct commit *commit) printf("\n"); } -static int update_index_refresh(int fd, struct lock_file *index_lock) +static int update_index_refresh(int fd, struct lock_file *index_lock, int flags) { int result; @@ -96,7 +96,8 @@ static int update_index_refresh(int fd, struct lock_file *index_lock) if (read_cache() < 0) return error("Could not read index"); - result = refresh_cache(REFRESH_SAY_CHANGED) ? 1 : 0; + + result = refresh_cache(flags) ? 1 : 0; if (write_cache(fd, active_cache, active_nr) || commit_locked_index(index_lock)) return error ("Could not refresh index"); @@ -128,7 +129,7 @@ static void update_index_from_diff(struct diff_queue_struct *q, } static int read_from_tree(const char *prefix, const char **argv, - unsigned char *tree_sha1) + unsigned char *tree_sha1, int refresh_flags) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd, index_was_discarded = 0; @@ -152,7 +153,7 @@ static int read_from_tree(const char *prefix, const char **argv, if (!index_was_discarded) /* The index is still clobbered from do_diff_cache() */ discard_cache(); - return update_index_refresh(index_fd, lock); + return update_index_refresh(index_fd, lock, refresh_flags); } static void prepend_reflog_action(const char *action, char *buf, size_t size) @@ -246,7 +247,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - return read_from_tree(prefix, argv + i, sha1); + return read_from_tree(prefix, argv + i, sha1, + quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -286,7 +288,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ - update_index_refresh(0, NULL); + update_index_refresh(0, NULL, + quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); break; } diff --git a/builtin-tag.c b/builtin-tag.c index c2cca6cb6d..325b1b2632 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -23,59 +23,6 @@ static const char * const git_tag_usage[] = { static char signingkey[1000]; -void launch_editor(const char *path, struct strbuf *buffer, const char *const *env) -{ - const char *editor, *terminal; - - editor = getenv("GIT_EDITOR"); - if (!editor && editor_program) - editor = editor_program; - if (!editor) - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - - terminal = getenv("TERM"); - if (!editor && (!terminal || !strcmp(terminal, "dumb"))) { - fprintf(stderr, - "Terminal is dumb but no VISUAL nor EDITOR defined.\n" - "Please supply the message using either -m or -F option.\n"); - exit(1); - } - - if (!editor) - editor = "vi"; - - if (strcmp(editor, ":")) { - size_t len = strlen(editor); - int i = 0; - const char *args[6]; - struct strbuf arg0; - - strbuf_init(&arg0, 0); - if (strcspn(editor, "$ \t'") != len) { - /* there are specials */ - strbuf_addf(&arg0, "%s \"$@\"", editor); - args[i++] = "sh"; - args[i++] = "-c"; - args[i++] = arg0.buf; - } - args[i++] = editor; - args[i++] = path; - args[i] = NULL; - - if (run_command_v_opt_cd_env(args, 0, NULL, env)) - die("There was a problem with the editor %s.", editor); - strbuf_release(&arg0); - } - - if (!buffer) - return; - if (strbuf_read_file(buffer, path, 0) < 0) - die("could not read message file '%s': %s", - path, strerror(errno)); -} - struct tag_filter { const char *pattern; int lines; @@ -348,7 +295,11 @@ static void create_tag(const unsigned char *object, const char *tag, write_or_die(fd, tag_template, strlen(tag_template)); close(fd); - launch_editor(path, buf, NULL); + if (launch_editor(path, buf, NULL)) { + fprintf(stderr, + "Please supply the message using either -m or -F option.\n"); + exit(1); + } unlink(path); free(path); diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 13a6c6203e..a9b02fa32f 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -16,15 +16,13 @@ static const char deadchild[] = static const char lostchild[] = "git upload-archive: archiver process was lost"; +#define MAX_ARGS (64) static int run_upload_archive(int argc, const char **argv, const char *prefix) { - const struct archiver *ar; - struct archiver_args args; const char *sent_argv[MAX_ARGS]; const char *arg_cmd = "argument "; char *p, buf[4096]; - int treeish_idx; int sent_argc; int len; @@ -48,7 +46,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) if (len == 0) break; /* got a flush */ if (sent_argc > MAX_ARGS - 2) - die("Too many options (>29)"); + die("Too many options (>%d)", MAX_ARGS - 2); if (p[len-1] == '\n') { p[--len] = 0; @@ -66,12 +64,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) sent_argv[sent_argc] = NULL; /* parse all options sent by the client */ - treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar, &args); - - parse_treeish_arg(sent_argv + treeish_idx, &args, prefix); - parse_pathspec_arg(sent_argv + treeish_idx + 1, &args); - - return ar->write_archive(&args); + return write_archive(sent_argc, sent_argv, prefix, 0); } static void error_clnt(const char *fmt, ...) diff --git a/builtin.h b/builtin.h index 0e605d4f4a..f3502d305e 100644 --- a/builtin.h +++ b/builtin.h @@ -18,6 +18,7 @@ extern int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out); extern int commit_tree(const char *msg, unsigned char *tree, struct commit_list *parents, unsigned char *ret); +extern int check_pager_config(const char *cmd); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix); diff --git a/commit.c b/commit.c index 5148ec5527..dc0c5bfdab 100644 --- a/commit.c +++ b/commit.c @@ -436,8 +436,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) /* Mark them and clear the indegree */ for (next = orig; next; next = next->next) { struct commit *commit = next->item; - commit->object.flags |= TOPOSORT; - commit->indegree = 0; + commit->indegree = 1; } /* update the indegree */ @@ -446,7 +445,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) while (parents) { struct commit *parent = parents->item; - if (parent->object.flags & TOPOSORT) + if (parent->indegree) parent->indegree++; parents = parents->next; } @@ -464,7 +463,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) for (next = orig; next; next = next->next) { struct commit *commit = next->item; - if (!commit->indegree) + if (commit->indegree == 1) insert = &commit_list_insert(commit, insert)->next; } @@ -486,7 +485,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) for (parents = commit->parents; parents ; parents = parents->next) { struct commit *parent=parents->item; - if (!(parent->object.flags & TOPOSORT)) + if (!parent->indegree) continue; /* @@ -494,7 +493,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * when all their children have been emitted thereby * guaranteeing topological order. */ - if (!--parent->indegree) { + if (--parent->indegree == 1) { if (!lifo) insert_by_date(parent, &work); else @@ -505,7 +504,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * work_item is a commit all of whose children * have already been emitted. we can emit it now. */ - commit->object.flags &= ~TOPOSORT; + commit->indegree = 0; *pptr = work_item; pptr = &work_item->next; } diff --git a/compat/mingw.h b/compat/mingw.h index 8ffec51e73..290a9e6f82 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -223,3 +223,15 @@ void mingw_open_html(const char *path); char **copy_environ(void); void free_environ(char **env); char **env_setenv(char **env, const char *name); + +/* + * A replacement of main() that ensures that argv[0] has a path + */ + +#define main(c,v) main(int argc, const char **argv) \ +{ \ + static int mingw_main(); \ + argv[0] = xstrdup(_pgmptr); \ + return mingw_main(argc, argv); \ +} \ +static int mingw_main(c,v) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2edb341b57..40b3d99737 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -489,7 +489,7 @@ _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" if [ -d "$dir"/rebase-apply ]; then - __gitcomp "--skip --resolved" + __gitcomp "--skip --resolved --abort" return fi case "$cur" in @@ -626,6 +626,8 @@ _git_bundle () _git_checkout () { + __git_has_doubledash && return + __gitcomp "$(__git_refs)" } @@ -1170,6 +1172,20 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_rm () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_shortlog () { __git_has_doubledash && return @@ -1210,6 +1226,22 @@ _git_show () __git_complete_file } +_git_show_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --remotes --topo-order --current --more= + --list --independent --merge-base --no-name + --sha1-name --topics --reflog + " + return + ;; + esac + __git_complete_revlist +} + _git_stash () { local subcommands='save list show apply clear drop pop create' @@ -1425,10 +1457,11 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; - show-branch) _git_log ;; + show-branch) _git_show_branch ;; stash) _git_stash ;; submodule) _git_submodule ;; svn) _git_svn ;; diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index ea8c1b2f60..a13bb6afec 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -933,7 +933,7 @@ while ($to_rev < $opt_l) { $to_rev = $from_rev + $repack_after; $to_rev = $opt_l if $opt_l < $to_rev; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + $svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all); my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 87ca51e401..6ae0429c2d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -906,7 +906,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) contents[stat['depotFile']] = text diff --git a/daemon.c b/daemon.c index 7df41a6a49..4540e8df5a 100644 --- a/daemon.c +++ b/daemon.c @@ -16,6 +16,7 @@ static int log_syslog; static int verbose; static int reuseaddr; +static int child_handler_pipe[2]; static const char daemon_usage[] = "git daemon [--verbose] [--syslog] [--export-all]\n" @@ -788,6 +789,7 @@ static void child_handler(int signo) pid = -pid; dead_child[reaped % MAX_CHILDREN] = pid; children_reaped = reaped + 1; + write(child_handler_pipe[1], &status, 1); continue; } break; @@ -933,29 +935,24 @@ static int service_loop(int socknum, int *socklist) struct pollfd *pfd; int i; - pfd = xcalloc(socknum, sizeof(struct pollfd)); + if (pipe(child_handler_pipe) < 0) + die ("Could not set up pipe for child handler"); + + pfd = xcalloc(socknum + 1, sizeof(struct pollfd)); for (i = 0; i < socknum; i++) { pfd[i].fd = socklist[i]; pfd[i].events = POLLIN; } + pfd[socknum].fd = child_handler_pipe[0]; + pfd[socknum].events = POLLIN; signal(SIGCHLD, child_handler); for (;;) { int i; - int timeout; - /* - * This 1-sec timeout could lead to idly looping but it is - * here so that children culled in child_handler() are reported - * without too much delay. We could probably set up a pipe - * to ourselves that we poll, and write to the fd from child_handler() - * to wake us up (and consume it when the poll() returns... - */ - timeout = (children_spawned != children_deleted) ? 1000 : -1; - i = poll(pfd, socknum, timeout); - if (i < 0) { + if (poll(pfd, socknum + 1, -1) < 0) { if (errno != EINTR) { error("poll failed, resuming: %s", strerror(errno)); @@ -963,9 +960,9 @@ static int service_loop(int socknum, int *socklist) } continue; } - if (i == 0) { + if (pfd[socknum].revents & POLLIN) { + read(child_handler_pipe[0], &i, 1); check_dead_children(); - continue; } for (i = 0; i < socknum; i++) { diff --git a/editor.c b/editor.c new file mode 100644 index 0000000000..eebc3e95fe --- /dev/null +++ b/editor.c @@ -0,0 +1,56 @@ +#include "cache.h" +#include "strbuf.h" +#include "run-command.h" + +int launch_editor(const char *path, struct strbuf *buffer, const char *const *env) +{ + const char *editor, *terminal; + + editor = getenv("GIT_EDITOR"); + if (!editor && editor_program) + editor = editor_program; + if (!editor) + editor = getenv("VISUAL"); + if (!editor) + editor = getenv("EDITOR"); + + terminal = getenv("TERM"); + if (!editor && (!terminal || !strcmp(terminal, "dumb"))) + return error("Terminal is dumb but no VISUAL nor EDITOR defined."); + + if (!editor) + editor = "vi"; + + if (strcmp(editor, ":")) { + size_t len = strlen(editor); + int i = 0; + int failed; + const char *args[6]; + struct strbuf arg0; + + strbuf_init(&arg0, 0); + if (strcspn(editor, "$ \t'") != len) { + /* there are specials */ + strbuf_addf(&arg0, "%s \"$@\"", editor); + args[i++] = "sh"; + args[i++] = "-c"; + args[i++] = arg0.buf; + } + args[i++] = editor; + args[i++] = path; + args[i] = NULL; + + failed = run_command_v_opt_cd_env(args, 0, NULL, env); + strbuf_release(&arg0); + if (failed) + return error("There was a problem with the editor '%s'.", + editor); + } + + if (!buffer) + return 0; + if (strbuf_read_file(buffer, path, 0) < 0) + return error("could not read file '%s': %s", + path, strerror(errno)); + return 0; +} diff --git a/exec_cmd.c b/exec_cmd.c index 8899e31b3b..0ed768ddc0 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -5,51 +5,23 @@ extern char **environ; static const char *argv_exec_path; - -static const char *builtin_exec_path(void) -{ -#ifndef __MINGW32__ - return GIT_EXEC_PATH; -#else - int len; - char *p, *q, *sl; - static char *ep; - if (ep) - return ep; - - len = strlen(_pgmptr); - if (len < 2) - return ep = "."; - - p = ep = xmalloc(len+1); - q = _pgmptr; - sl = NULL; - /* copy program name, turn '\\' into '/', skip last part */ - while ((*p = *q)) { - if (*q == '\\' || *q == '/') { - *p = '/'; - sl = p; - } - p++, q++; - } - if (sl) - *sl = '\0'; - else - ep[0] = '.', ep[1] = '\0'; - return ep; -#endif -} +static const char *argv0_path; const char *system_path(const char *path) { - if (!is_absolute_path(path)) { + if (!is_absolute_path(path) && argv0_path) { struct strbuf d = STRBUF_INIT; - strbuf_addf(&d, "%s/%s", git_exec_path(), path); + strbuf_addf(&d, "%s/%s", argv0_path, path); path = strbuf_detach(&d, NULL); } return path; } +void git_set_argv0_path(const char *path) +{ + argv0_path = path; +} + void git_set_argv_exec_path(const char *exec_path) { argv_exec_path = exec_path; @@ -69,7 +41,7 @@ const char *git_exec_path(void) return env; } - return builtin_exec_path(); + return system_path(GIT_EXEC_PATH); } static void add_path(struct strbuf *out, const char *path) @@ -78,13 +50,13 @@ static void add_path(struct strbuf *out, const char *path) if (is_absolute_path(path)) strbuf_addstr(out, path); else - strbuf_addstr(out, make_absolute_path(path)); + strbuf_addstr(out, make_nonrelative_path(path)); strbuf_addch(out, PATH_SEP); } } -void setup_path(const char *cmd_path) +void setup_path(void) { const char *old_path = getenv("PATH"); struct strbuf new_path; @@ -93,8 +65,8 @@ void setup_path(const char *cmd_path) add_path(&new_path, argv_exec_path); add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); - add_path(&new_path, builtin_exec_path()); - add_path(&new_path, cmd_path); + add_path(&new_path, system_path(GIT_EXEC_PATH)); + add_path(&new_path, argv0_path); if (old_path) strbuf_addstr(&new_path, old_path); diff --git a/exec_cmd.h b/exec_cmd.h index 7eb94e5e11..0c46cd5636 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -2,8 +2,9 @@ #define GIT_EXEC_CMD_H extern void git_set_argv_exec_path(const char *exec_path); +extern void git_set_argv0_path(const char *path); extern const char* git_exec_path(void); -extern void setup_path(const char *); +extern void setup_path(void); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); extern const char *system_path(const char *path); diff --git a/git-am.sh b/git-am.sh index 7864b5f588..6aa819280e 100755 --- a/git-am.sh +++ b/git-am.sh @@ -6,8 +6,7 @@ SUBDIRECTORY_OK=Yes OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ git am [options] [|...] -git am [options] --resolved -git am [options] --skip +git am [options] (--resolved | --skip | --abort) -- d,dotest= (removed -- do not use) i,interactive run interactively @@ -456,7 +455,7 @@ do stop_here $this fi - printf 'Applying %s\n' "$FIRSTLINE" + printf 'Applying: %s\n' "$FIRSTLINE" case "$resolved" in '') diff --git a/git-filter-branch.sh b/git-filter-branch.sh index d04c346e12..182822a24e 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -97,9 +97,11 @@ USAGE="[--env-filter ] [--tree-filter ] \ OPTIONS_SPEC= . git-sh-setup -git diff-files --quiet && +if [ "$(is_bare_repository)" = false ]; then + git diff-files --quiet && git diff-index --cached --quiet HEAD -- || die "Cannot rewrite branch(es) with a dirty working directory." +fi tempdir=.git-rewrite filter_env= @@ -434,18 +436,20 @@ rm -rf "$tempdir" trap - 0 -unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE -test -z "$ORIG_GIT_DIR" || { - GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR -} -test -z "$ORIG_GIT_WORK_TREE" || { - GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && - export GIT_WORK_TREE -} -test -z "$ORIG_GIT_INDEX_FILE" || { - GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" && - export GIT_INDEX_FILE -} -git read-tree -u -m HEAD +if [ "$(is_bare_repository)" = false ]; then + unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE + test -z "$ORIG_GIT_DIR" || { + GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR + } + test -z "$ORIG_GIT_WORK_TREE" || { + GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && + export GIT_WORK_TREE + } + test -z "$ORIG_GIT_INDEX_FILE" || { + GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" && + export GIT_INDEX_FILE + } + git read-tree -u -m HEAD +fi exit $ret diff --git a/git-pull.sh b/git-pull.sh index 6afd4e2f4e..75c36100a2 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -107,9 +107,9 @@ error_on_no_merge_candidates () { } test true = "$rebase" && { - git update-index --refresh && - git diff-files --quiet && - git diff-index --cached --quiet HEAD -- || + git update-index --ignore-submodules --refresh && + git diff-files --ignore-submodules --quiet && + git diff-index --ignore-submodules --cached --quiet HEAD -- || die "refusing to pull with rebase: your working tree is not up-to-date" . git-parse-remote && diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index e63a864c7b..4e334ba41d 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -277,7 +277,7 @@ do_next () { die_with_patch $sha1 "Could not apply $sha1... $rest" make_patch $sha1 : > "$DOTEST"/amend - warn + warn "Stopped at $sha1... $rest" warn "You can amend the commit now, with" warn warn " git commit --amend" diff --git a/git-send-email.perl b/git-send-email.perl index 2e4a44ad29..d2fd899076 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -882,7 +882,7 @@ foreach my $t (@files) { } elsif (/^Content-type:/i) { $has_content_type = 1; - if (/charset="?[^ "]+/) { + if (/charset="?([^ "]+)/) { $body_encoding = $1; } push @xh, $_; diff --git a/git-stash.sh b/git-stash.sh index e4cb6c3e4b..d4609ed66e 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -93,7 +93,7 @@ save_stash () { shift esac - stash_msg="$1" + stash_msg="$*" if no_changes then @@ -267,7 +267,7 @@ show) ;; save) shift - save_stash "$*" + save_stash "$@" ;; apply) shift diff --git a/git-svn.perl b/git-svn.perl index 2e0e55242f..cf6dbbc427 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3340,6 +3340,7 @@ sub new { $self->{rm} = { }; $self->{path_prefix} = length $self->{svn_path} ? "$self->{svn_path}/" : ''; + $self->{config} = $opts->{config}; return $self; } @@ -3528,6 +3529,57 @@ sub ensure_path { return $bat->{$c}; } +# Subroutine to convert a globbing pattern to a regular expression. +# From perl cookbook. +sub glob2pat { + my $globstr = shift; + my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']'); + $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge; + return '^' . $globstr . '$'; +} + +sub check_autoprop { + my ($self, $pattern, $properties, $file, $fbat) = @_; + # Convert the globbing pattern to a regular expression. + my $regex = glob2pat($pattern); + # Check if the pattern matches the file name. + if($file =~ m/($regex)/) { + # Parse the list of properties to set. + my @props = split(/;/, $properties); + foreach my $prop (@props) { + # Parse 'name=value' syntax and set the property. + if ($prop =~ /([^=]+)=(.*)/) { + my ($n,$v) = ($1,$2); + for ($n, $v) { + s/^\s+//; s/\s+$//; + } + $self->change_file_prop($fbat, $n, $v); + } + } + } +} + +sub apply_autoprops { + my ($self, $file, $fbat) = @_; + my $conf_t = ${$self->{config}}{'config'}; + no warnings 'once'; + # Check [miscellany]/enable-auto-props in svn configuration. + if (SVN::_Core::svn_config_get_bool( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY, + $SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, + 0)) { + # Auto-props are enabled. Enumerate them to look for matches. + my $callback = sub { + $self->check_autoprop($_[0], $_[1], $file, $fbat); + }; + SVN::_Core::svn_config_enumerate( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS, + $callback); + } +} + sub A { my ($self, $m) = @_; my ($dir, $file) = split_path($m->{file_b}); @@ -3535,6 +3587,7 @@ sub A { my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, undef, -1); print "\tA\t$m->{file_b}\n" unless $::_q; + $self->apply_autoprops($file, $fbat); $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } diff --git a/git.c b/git.c index 1bfd271a71..37b1d76a08 100644 --- a/git.c +++ b/git.c @@ -418,7 +418,6 @@ int main(int argc, const char **argv) { const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help"; char *slash = (char *)cmd + strlen(cmd); - const char *cmd_path = NULL; int done_alias = 0; /* @@ -431,7 +430,7 @@ int main(int argc, const char **argv) while (cmd <= slash && !is_dir_sep(*slash)); if (cmd <= slash) { *slash++ = 0; - cmd_path = cmd; + git_set_argv0_path(cmd); cmd = slash; } @@ -475,7 +474,7 @@ int main(int argc, const char **argv) * environment, and the $(gitexecdir) from the Makefile at build * time. */ - setup_path(cmd_path); + setup_path(); while (1) { /* See if it's an internal command */ diff --git a/index-pack.c b/index-pack.c index ac20a46d15..52064befdb 100644 --- a/index-pack.c +++ b/index-pack.c @@ -699,6 +699,10 @@ static struct object_entry *append_obj_to_pack( write_or_die(output_fd, header, n); obj[0].idx.crc32 = crc32(0, Z_NULL, 0); obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n); + obj[0].size = size; + obj[0].hdr_size = n; + obj[0].type = type; + obj[0].real_type = type; obj[1].idx.offset = obj[0].idx.offset + n; obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32); hashcpy(obj->idx.sha1, sha1); diff --git a/path.c b/path.c index 504eae061f..9df447bd6d 100644 --- a/path.c +++ b/path.c @@ -291,42 +291,6 @@ int adjust_shared_perm(const char *path) return 0; } -static const char *get_pwd_cwd(void) -{ - static char cwd[PATH_MAX + 1]; - char *pwd; - struct stat cwd_stat, pwd_stat; - if (getcwd(cwd, PATH_MAX) == NULL) - return NULL; - pwd = getenv("PWD"); - if (pwd && strcmp(pwd, cwd)) { - stat(cwd, &cwd_stat); - if (!stat(pwd, &pwd_stat) && - pwd_stat.st_dev == cwd_stat.st_dev && - pwd_stat.st_ino == cwd_stat.st_ino) { - strlcpy(cwd, pwd, PATH_MAX); - } - } - return cwd; -} - -const char *make_nonrelative_path(const char *path) -{ - static char buf[PATH_MAX + 1]; - - if (is_absolute_path(path)) { - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); - } else { - const char *cwd = get_pwd_cwd(); - if (!cwd) - die("Cannot determine the current working directory"); - if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); - } - return buf; -} - const char *make_relative_path(const char *abs, const char *base) { static char buf[PATH_MAX + 1]; diff --git a/perl/Makefile b/perl/Makefile index f7dbfca616..90b1e2ba6e 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -26,13 +26,18 @@ ifdef NO_PERL_MAKEMAKER # $(instdir_SQ) and split it at colons. instdir_SQ = $(subst ','\'',$(shell cmd /x/d/c "set TEMP=$(prefix)/lib && sh -c 'echo \$$TEMP'")) $(makfile): ../GIT-CFLAGS Makefile - echo all: > $@ - echo ' :' >> $@ + echo all: private-Error.pm Git.pm > $@ + echo ' mkdir -p blib/lib' >> $@ + echo ' $(RM) blib/lib/Git.pm; cp Git.pm blib/lib/' >> $@ + echo ' $(RM) blib/lib/Error.pm' >> $@ + '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \ + echo ' cp private-Error.pm blib/lib/Error.pm' >> $@ echo install: >> $@ echo ' mkdir -p $(instdir_SQ)' >> $@ echo ' $(RM) $(instdir_SQ)/Git.pm; cp Git.pm $(instdir_SQ)' >> $@ - echo ' $(RM) $(instdir_SQ)/Error.pm; \ - cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@ + echo ' $(RM) $(instdir_SQ)/Error.pm' >> $@ + '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \ + echo ' cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@ echo instlibdir: >> $@ echo ' echo $(instdir_SQ)' >> $@ else diff --git a/receive-pack.c b/receive-pack.c index fa653b49fe..d44c19e6b5 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -482,7 +482,7 @@ int main(int argc, char **argv) if (!dir) usage(receive_pack_usage); - setup_path(NULL); + setup_path(); if (!enter_repo(dir, 0)) die("'%s': unable to chdir or not a git archive", dir); diff --git a/revision.h b/revision.h index fa68c65142..f64e8ce7ff 100644 --- a/revision.h +++ b/revision.h @@ -12,8 +12,7 @@ #define CHILD_SHOWN (1u<<6) #define ADDED (1u<<7) /* Parents already parsed and added? */ #define SYMMETRIC_LEFT (1u<<8) -#define TOPOSORT (1u<<9) /* In the active toposort list.. */ -#define ALL_REV_FLAGS ((1u<<10)-1) +#define ALL_REV_FLAGS ((1u<<9)-1) struct rev_info; struct log_info; diff --git a/sha1_name.c b/sha1_name.c index b0b2167578..4fb77f8863 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -273,7 +273,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) const char *ref, *it; strcpy(path, mkpath(*p, len, str)); - ref = resolve_ref(path, hash, 0, NULL); + ref = resolve_ref(path, hash, 1, NULL); if (!ref) continue; if (!stat(git_path("logs/%s", path), &st) && diff --git a/shell.c b/shell.c index 91ca7de082..6a48de05ff 100644 --- a/shell.c +++ b/shell.c @@ -15,7 +15,7 @@ static int do_generic_cmd(const char *me, char *arg) { const char *my_argv[4]; - setup_path(NULL); + setup_path(); if (!arg || !(arg = sq_dequote(arg))) die("bad argument"); if (prefixcmp(me, "git-")) @@ -37,7 +37,7 @@ static int do_cvs_cmd(const char *me, char *arg) if (!arg || strcmp(arg, "server")) die("git-cvsserver only handles server: %s", arg); - setup_path(NULL); + setup_path(); return execv_git_cmd(cvsserver_argv); } diff --git a/strbuf.h b/strbuf.h index 0c6ffad53a..eba7ba423a 100644 --- a/strbuf.h +++ b/strbuf.h @@ -123,6 +123,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); extern int strbuf_getline(struct strbuf *, FILE *, int); extern void stripspace(struct strbuf *buf, int skip_comments); -extern void launch_editor(const char *path, struct strbuf *buffer, const char *const *env); +extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); #endif /* STRBUF_H */ diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index c5dbc724b6..4db4ac44c9 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh @@ -66,7 +66,7 @@ test_expect_success 'check hash-object' ' test_expect_success 'check cat-file' ' git cat-file blob $SHA >actual && - diff -u bar actual + test_cmp bar actual ' test_expect_success 'check update-index' ' diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index a1cbe6832e..25628bfaa3 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -118,7 +118,7 @@ test_expect_success \ git update-index --add frotz && git read-tree -m -u $treeH $treeM && git ls-files --stage >6.out && - diff -U0 M.out 6.out && + test_cmp M.out 6.out && check_cache_at frotz clean && sum bozbar frotz nitfol >actual3.sum && cmp M.sum actual3.sum && @@ -135,7 +135,7 @@ test_expect_success \ echo frotz frotz >frotz && git read-tree -m -u $treeH $treeM && git ls-files --stage >7.out && - diff -U0 M.out 7.out && + test_cmp M.out 7.out && check_cache_at frotz dirty && sum bozbar frotz nitfol >actual7.sum && if cmp M.sum actual7.sum; then false; else :; fi && @@ -270,7 +270,7 @@ test_expect_success \ git update-index --add bozbar && git read-tree -m -u $treeH $treeM && git ls-files --stage >18.out && - diff -U0 M.out 18.out && + test_cmp M.out 18.out && check_cache_at bozbar clean && sum bozbar frotz nitfol >actual18.sum && cmp M.sum actual18.sum' @@ -284,7 +284,7 @@ test_expect_success \ echo gnusto gnusto >bozbar && git read-tree -m -u $treeH $treeM && git ls-files --stage >19.out && - diff -U0 M.out 19.out && + test_cmp M.out 19.out && check_cache_at bozbar dirty && sum frotz nitfol >actual19.sum && grep -v bozbar M.sum > expected19.sum && @@ -303,7 +303,7 @@ test_expect_success \ git update-index --add bozbar && git read-tree -m -u $treeH $treeM && git ls-files --stage >20.out && - diff -U0 M.out 20.out && + test_cmp M.out 20.out && check_cache_at bozbar clean && sum bozbar frotz nitfol >actual20.sum && cmp M.sum actual20.sum' @@ -344,7 +344,7 @@ test_expect_success \ git update-index --add DF && git read-tree -m -u $treeDF $treeDFDF && git ls-files --stage >DFDFcheck.out && - diff -U0 DFDF.out DFDFcheck.out && + test_cmp DFDF.out DFDFcheck.out && check_cache_at DF/DF clean' test_done diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh new file mode 100755 index 0000000000..7cc0a3582e --- /dev/null +++ b/t/t2010-checkout-ambiguous.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='checkout and pathspecs/refspecs ambiguities' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo hello >world && + echo hello >all && + git add all world && + git commit -m initial && + git branch world +' + +test_expect_success 'reference must be a tree' ' + test_must_fail git checkout $(git hash-object ./all) -- +' + +test_expect_success 'branch switching' ' + test "refs/heads/master" = "$(git symbolic-ref HEAD)" && + git checkout world -- && + test "refs/heads/world" = "$(git symbolic-ref HEAD)" +' + +test_expect_success 'checkout world from the index' ' + echo bye > world && + git checkout -- world && + git diff --exit-code --quiet +' + +test_expect_success 'non ambiguous call' ' + git checkout all +' + +test_expect_success 'allow the most common case' ' + git checkout world && + test "refs/heads/world" = "$(git symbolic-ref HEAD)" +' + +test_expect_success 'check ambiguity' ' + test_must_fail git checkout world all +' + +test_expect_success 'disambiguate checking out from a tree-ish' ' + echo bye > world && + git checkout world -- world && + git diff --exit-code --quiet +' + +test_done diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh index 69ac97e648..973f8906b5 100755 --- a/t/t2201-add-update-typechange.sh +++ b/t/t2201-add-update-typechange.sh @@ -115,12 +115,12 @@ test_expect_success modify ' test_expect_success diff-files ' git diff-files --raw >actual && - diff -u expect-files actual + test_cmp expect-files actual ' test_expect_success diff-index ' git diff-index --raw HEAD -- >actual && - diff -u expect-index actual + test_cmp expect-index actual ' test_expect_success 'add -u' ' @@ -128,7 +128,7 @@ test_expect_success 'add -u' ' cp -p ".git/index" ".git/saved-index" && git add -u && git ls-files -s >actual && - diff -u expect-final actual + test_cmp expect-final actual ' test_expect_success 'commit -a' ' @@ -139,11 +139,11 @@ test_expect_success 'commit -a' ' fi && git commit -m "second" -a && git ls-files -s >actual && - diff -u expect-final actual && + test_cmp expect-final actual && rm -f .git/index && git read-tree HEAD && git ls-files -s >actual && - diff -u expect-final actual + test_cmp expect-final actual ' test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 5c685e48fb..7059ddc9da 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -202,7 +202,7 @@ test_expect_success \ test_expect_success \ 'branch from non-branch HEAD w/--track causes failure' \ - '!(git branch --track my10 HEAD^)' + 'test_must_fail git branch --track my10 HEAD^' # Keep this test last, as it changes the current branch cat >expect < file && + git add file && + test_tick && + git commit -m initial && + + for i in $numbers + do + git checkout -b branch$i master && + > file$i && + git add file$i && + test_tick && + git commit -m branch$i || break + done + +' + +cat > expect << EOF +! [branch1] branch1 + ! [branch2] branch2 + ! [branch3] branch3 + ! [branch4] branch4 + ! [branch5] branch5 + ! [branch6] branch6 + ! [branch7] branch7 + ! [branch8] branch8 + ! [branch9] branch9 + * [branch10] branch10 +---------- + * [branch10] branch10 + + [branch9] branch9 + + [branch8] branch8 + + [branch7] branch7 + + [branch6] branch6 + + [branch5] branch5 + + [branch4] branch4 + + [branch3] branch3 + + [branch2] branch2 ++ [branch1] branch1 ++++++++++* [branch10^] initial +EOF + +test_expect_success 'show-branch with more than 8 branches' ' + + git show-branch $(for i in $numbers; do echo branch$i; done) > out && + test_cmp expect out + +' + +test_done diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index 821604e0dc..1f8743bfa6 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -49,12 +49,12 @@ test_expect_success 'apply in reverse' ' test_expect_success 'setup separate repository lacking postimage' ' - git tar-tree initial initial | tar xf - && + git tar-tree initial initial | $TAR xf - && ( cd initial && git init && git add . ) && - git tar-tree second second | tar xf - && + git tar-tree second second | $TAR xf - && ( cd second && git init && git add . ) diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index 249093b6d0..7d86cdff64 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -17,6 +17,8 @@ test_expect_success setup ' for i in 2 3 4 5 6 do echo $i >>file-1 && + echo $i >otherfile-$i && + git add otherfile-$i && test_tick && git commit -a -m $i || break done && @@ -43,7 +45,7 @@ do test_expect_success "am$with3 --skip continue after failed am$with3" ' test_must_fail git-am$with3 --skip >output && - test "$(grep "^Applying" output)" = "Applying 6" && + test "$(grep "^Applying" output)" = "Applying: 6" && test_cmp file-2-expect file-2 && test ! -f .git/rr-cache/MERGE_RR ' diff --git a/t/t4202-log.sh b/t/t4202-log.sh index b53645417b..4c8af45f83 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -71,4 +71,5 @@ test_expect_success 'diff-filter=D' ' -test_done \ No newline at end of file +test_done + diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 098ca2decc..f8329efaf9 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -25,7 +25,6 @@ commit id embedding: ' . ./test-lib.sh -TAR=${TAR:-tar} UNZIP=${UNZIP:-unzip} test "$no_symlinks" && { @@ -84,7 +83,7 @@ test_expect_success \ test_expect_success \ 'validate file modification time' \ 'mkdir extract && - $TAR xf b.tar -C extract a/a && + "$TAR" xf b.tar -C extract a/a && perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime && echo "1117231200" >expected.mtime && diff expected.mtime b.mtime' @@ -96,7 +95,7 @@ test_expect_success \ test_expect_success \ 'extract tar archive' \ - '(cd b && $TAR xf -) not_ssh + echo "echo \"\$*\" > not_ssh_output" >> not_ssh + echo "exit 1" >> not_ssh + chmod +x not_ssh +' + +test_expect_success 'clone calls git-upload-pack unqualified with no -u option' ' + GIT_SSH=./not_ssh git clone localhost:/path/to/repo junk + echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected + test_cmp expected not_ssh_output +' + +test_expect_success 'clone calls specified git-upload-pack with -u option' ' + GIT_SSH=./not_ssh git clone -u /something/bin/git-upload-pack localhost:/path/to/repo junk + echo "localhost /something/bin/git-upload-pack '\''/path/to/repo'\''" >expected + test_cmp expected not_ssh_output +' + +test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index e26f726930..a0ab096c8f 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -38,6 +38,14 @@ test_expect_success 'result is really identical' ' test $H = $(git rev-parse HEAD) ' +test_expect_success 'rewrite bare repository identically' ' + (git config core.bare true && cd .git && git-filter-branch branch) +' +git config core.bare false +test_expect_success 'result is really identical' ' + test $H = $(git rev-parse HEAD) +' + test_expect_success 'rewrite, renaming a specific file' ' git-filter-branch -f --tree-filter "mv d doh || :" HEAD ' diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 5dd79bd018..4f3cd2642c 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -339,6 +339,6 @@ test_expect_success \ git checkout -b delete-me master && rm .git/refs/heads/delete-me && test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && - !(git checkout --track -b track)' + test_must_fail git checkout --track -b track' test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index c25eff9e46..4f2682ea80 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -228,10 +228,12 @@ EOF test_expect_success 'a SIGTERM should break locks' ' echo >>negative && - "$SHELL_PATH" -c '\'' + ! "$SHELL_PATH" -c '\'' echo kill -TERM $$ >> .git/FAKE_EDITOR - GIT_EDITOR=.git/FAKE_EDITOR exec git commit -a'\'' && exit 1 # should fail - ! test -f .git/index.lock + GIT_EDITOR=.git/FAKE_EDITOR + export GIT_EDITOR + exec git commit -a'\'' && + test ! -f .git/index.lock ' rm -f .git/MERGE_MSG .git/COMMIT_EDITMSG diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh index 6b9f6388c7..55aa6b5f27 100755 --- a/t/t7601-merge-pull-config.sh +++ b/t/t7601-merge-pull-config.sh @@ -112,6 +112,21 @@ test_expect_success 'setup conflicted merge' ' # recusive is choosen. test_expect_success 'merge picks up the best result' ' + git config --unset-all pull.twohead && + git reset --hard c5 && + git merge -s resolve c6 + resolve_count=$(conflict_count) && + git reset --hard c5 && + git merge -s recursive c6 + recursive_count=$(conflict_count) && + git reset --hard c5 && + git merge -s recursive -s resolve c6 + auto_count=$(conflict_count) && + test $auto_count = $recursive_count && + test $auto_count != $resolve_count +' + +test_expect_success 'merge picks up the best result (from config)' ' git config pull.twohead "recursive resolve" && git reset --hard c5 && git merge -s resolve c6 diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh new file mode 100755 index 0000000000..8223c5909e --- /dev/null +++ b/t/t9124-git-svn-dcommit-auto-props.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# +# Copyright (c) 2008 Brad King + +test_description='git-svn dcommit honors auto-props' + +. ./lib-git-svn.sh + +generate_auto_props() { +cat << EOF +[miscellany] +enable-auto-props=$1 +[auto-props] +*.sh = svn:mime-type=application/x-shellscript; svn:eol-style=LF +*.txt = svn:mime-type=text/plain; svn:eol-style = native +EOF +} + +test_expect_success 'initialize git-svn' ' + mkdir import && + ( + cd import && + echo foo >foo && + svn import -m "import for git-svn" . "$svnrepo" + ) && + rm -rf import && + git-svn init "$svnrepo" + git-svn fetch +' + +test_expect_success 'enable auto-props config' ' + cd "$gittestrepo" && + mkdir user && + generate_auto_props yes >user/config +' + +test_expect_success 'add files matching auto-props' ' + cd "$gittestrepo" && + echo "#!$SHELL_PATH" >exec1.sh && + chmod +x exec1.sh && + echo "hello" >hello.txt && + echo bar >bar && + git add exec1.sh hello.txt bar && + git commit -m "files for enabled auto-props" && + git svn dcommit --config-dir=user +' + +test_expect_success 'disable auto-props config' ' + cd "$gittestrepo" && + generate_auto_props no >user/config +' + +test_expect_success 'add files matching disabled auto-props' ' + cd "$gittestrepo" && + echo "#$SHELL_PATH" >exec2.sh && + chmod +x exec2.sh && + echo "world" >world.txt && + echo zot >zot && + git add exec2.sh world.txt zot && + git commit -m "files for disabled auto-props" && + git svn dcommit --config-dir=user +' + +test_expect_success 'check resulting svn repository' ' + mkdir work && + cd work && + svn co "$svnrepo" && + cd svnrepo && + + # Check properties from first commit. + test "x$(svn propget svn:executable exec1.sh)" = "x*" && + test "x$(svn propget svn:mime-type exec1.sh)" = \ + "xapplication/x-shellscript" && + test "x$(svn propget svn:mime-type hello.txt)" = "xtext/plain" && + test "x$(svn propget svn:eol-style hello.txt)" = "xnative" && + test "x$(svn propget svn:mime-type bar)" = "x" && + + # Check properties from second commit. + test "x$(svn propget svn:executable exec2.sh)" = "x*" && + test "x$(svn propget svn:mime-type exec2.sh)" = "x" && + test "x$(svn propget svn:mime-type world.txt)" = "x" && + test "x$(svn propget svn:eol-style world.txt)" = "x" && + test "x$(svn propget svn:mime-type zot)" = "x" +' + +test_done diff --git a/upload-pack.c b/upload-pack.c index 9f82941f8b..c911e70c9a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -638,7 +638,7 @@ int main(int argc, char **argv) if (i != argc-1) usage(upload_pack_usage); - setup_path(NULL); + setup_path(); dir = argv[i];