diff options
41 files changed, 622 insertions, 244 deletions
diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes-1.6.0.txt index 5292bd730c..03e3a59ff5 100644 --- a/Documentation/RelNotes-1.6.0.txt +++ b/Documentation/RelNotes-1.6.0.txt @@ -7,20 +7,16 @@ User visible changes [[Note that none of these are not merged to 'master' as of this writing but they will be before 1.6.0 happens]] -With default Makefile settings, most of the programs are now installed -outside your $PATH, except for "git", "gitk", "git-gui" and some server -side programs that needs to be accessible when connecting over ssh. - -When talking to remote repository over ssh, necessary server side programs -are now invoked with "git $program" notation, not with "git-$program" -notation. This should work with both servers running older git where you -had all of these programs installed on $PATH, or newer git where you have -only "git" on $PATH. However, if the remote side is running a custom -software that restricts programs you can run over ssh, it might cause -problems. Use --upload-pack="git-upload-pack" (when using ls-remote, -fetch and pull on the client side), --receive-pack="git-receive-pack" -(when using push on the client side), or --exec="git-upload-archive" (when -using git-archive) as appropriate when talking to such a remote. +With the default Makefile settings, most of the programs are now +installed outside your $PATH, except for "git", "gitk", "git-gui" and +some server side programs that need to be accessible for technical +reasons. Invoking a git subcommand as "git-xyzzy" from the command +line has been deprecated since early 2006 (and officially announced in +1.5.4 release notes); use of them from your scripts after adding +output from "git --exec-path" to the $PATH is still supported in this +release, but users are again strongly encouraged to adjust their +scripts to use "git xyzzy" form, as we will stop installing +"git-xyzzy" hardlinks for built-in commands in later releases. Source changes needed for porting to MinGW environment are now all in the main git.git codebase. @@ -31,6 +27,9 @@ Updates since v1.5.6 (subsystems) +* git-p4 in contrib learned "allowSubmit" configuration to control on + which branch to allow "submit" subcommand. + (portability) * Sample hook scripts shipped in templates/ are now suffixed with @@ -47,7 +46,13 @@ Updates since v1.5.6 * Updated howto/update-hook-example -(performance, robustness etc.) +* Got rid of usage of "git-foo" from the tutorial. + +* Disambiguating "--" between revs and paths is finally documented. + +(performance, robustness, sanity etc.) + +* even more documentation pages are now accessible via "man" and "git help". * reduced excessive inlining to shrink size of the "git" binary. @@ -67,6 +72,9 @@ Updates since v1.5.6 objects created will be fsync'ed (this is only useful on filesystems that does not order data writes properly). +* "git commit-tree" plumbing can make Octopus with more than 16 parents. + "git commit" has been capable of this for quite some time. + (usability, bells and whistles) * git-archive can be told to omit certain paths from its output using @@ -100,6 +108,6 @@ this release, unless otherwise noted. --- exec >/var/tmp/1 -O=v1.5.6.1-77-gf9a08f6 +O=v1.5.6.1-104-ga08b868 echo O=$(git describe refs/heads/master) git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 10c1a151a4..40d43b78ee 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -8,6 +8,7 @@ # the command. [attributes] +asterisk=* plus=+ caret=^ startsb=[ diff --git a/Documentation/config.txt b/Documentation/config.txt index 90c8a45a2f..561ff645f9 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -945,9 +945,17 @@ pack.indexVersion:: legacy pack index used by Git versions prior to 1.5.2, and 2 for the new pack index with capabilities for packs larger than 4 GB as well as proper protection against the repacking of corrupted - packs. Version 2 is selected and this config option ignored - whenever the corresponding pack is larger than 2 GB. Otherwise - the default is 1. + packs. Version 2 is the default. Note that version 2 is enforced + and this config option ignored whenever the corresponding pack is + larger than 2 GB. ++ +If you have an old git that does not understand the version 2 `{asterisk}.idx` file, +cloning or fetching over a non native protocol (e.g. "http" and "rsync") +that will copy both `{asterisk}.pack` file and corresponding `{asterisk}.idx` file from the +other side may give you a repository that cannot be accessed with your +older version of git. If the `{asterisk}.pack` file is smaller than 2 GB, however, +you can use linkgit:git-index-pack[1] on the *.pack file to regenerate +the `{asterisk}.idx` file. pack.packSizeLimit:: The default maximum size of a pack. This setting only affects @@ -1004,12 +1012,12 @@ remotes.<group>:: <group>". See linkgit:git-remote[1]. repack.usedeltabaseoffset:: - Allow linkgit:git-repack[1] to create packs that uses - delta-base offset. Defaults to false. - -show.difftree:: - The default linkgit:git-diff-tree[1] arguments to be used - for linkgit:git-show[1]. + By default, linkgit:git-repack[1] creates packs that use + delta-base offset. If you need to share your repository with + git older than version 1.4.4, either directly or via a dumb + protocol such as http, then you need to set this option to + "false" and repack. Access from old git versions over the + native protocol are unaffected by this option. showbranch.default:: The default set of branches for linkgit:git-show-branch[1]. @@ -1075,10 +1083,6 @@ user.signingkey:: unchanged to gpg's --local-user parameter, so you may specify a key using any method that gpg supports. -whatchanged.difftree:: - The default linkgit:git-diff-tree[1] arguments to be used - for linkgit:git-whatchanged[1]. - imap:: The configuration variables in the 'imap' section are described in linkgit:git-imap-send[1]. diff --git a/Documentation/git-parse-remote.txt b/Documentation/git-parse-remote.txt index 951dbd6c83..421312eca9 100644 --- a/Documentation/git-parse-remote.txt +++ b/Documentation/git-parse-remote.txt @@ -8,7 +8,7 @@ git-parse-remote - Routines to help parsing remote repository access parameters SYNOPSIS -------- -'. git-parse-remote' +'. "$(git --exec-path)/git-parse-remote"' DESCRIPTION ----------- diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 9e273bc5a6..59e95adf42 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -184,7 +184,10 @@ blobs contained in a commit. second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value of the ref at a prior point in time. This suffix may only be used immediately following a ref name and the ref must have an - existing log ($GIT_DIR/logs/<ref>). + existing log ($GIT_DIR/logs/<ref>). Note that this looks up the state + of your *local* ref at a given time; e.g., what was in your local + `master` branch last week. If you want to look at commits made during + certain times, see `--since` and `--until`. * A ref followed by the suffix '@' with an ordinal specification enclosed in a brace pair (e.g. '\{1\}', '\{15\}') to specify diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 251d661afd..dc7eb7bd4e 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -133,10 +133,13 @@ or on the command line. If a username has been specified (with specified (with --smtp-pass or a configuration variable), then the user is prompted for a password while the input is masked for privacy. +--smtp-encryption:: + Specify the encryption to use, either 'ssl' or 'tls'. Any other + value reverts to plain SMTP. Default is the value of + 'sendemail.smtpencryption'. + --smtp-ssl:: - If set, connects to the SMTP server using SSL. - Default is the value of the 'sendemail.smtpssl' configuration value; - if that is unspecified, does not use SSL. + Legacy alias for '--smtp-encryption=ssl'. --subject:: Specify the initial subject of the email thread. @@ -229,8 +232,13 @@ sendemail.smtpuser:: sendemail.smtppass:: Default SMTP-AUTH password. +sendemail.smtpencryption:: + Default encryption method. Use 'ssl' for SSL (and specify an + appropriate port), or 'tls' for TLS. Takes precedence over + 'smtpssl' if both are specified. + sendemail.smtpssl:: - Boolean value specifying the default to the '--smtp-ssl' parameter. + Legacy boolean that sets 'smtpencryption=ssl' if enabled. Author ------ diff --git a/Documentation/git-sh-setup.txt b/Documentation/git-sh-setup.txt index c543170342..6731f9ac4c 100644 --- a/Documentation/git-sh-setup.txt +++ b/Documentation/git-sh-setup.txt @@ -7,7 +7,7 @@ git-sh-setup - Common git shell script setup code SYNOPSIS -------- -'git-sh-setup' +'. "$(git --exec-path)/git-sh-setup"' DESCRIPTION ----------- diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 97bed54fbd..c350ad0f83 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -513,7 +513,7 @@ have each person clone that repository with 'git clone': cd project git-init git remote add origin server:/pub/project - git config --add remote.origin.fetch=+refs/remotes/*:refs/remotes/* + git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*' git fetch # Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server) git-svn init http://svn.foo.org/project diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 8fb5d889e5..2316049865 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -13,8 +13,37 @@ gitcli DESCRIPTION ----------- -This manual describes best practice in how to use git CLI. Here are -the rules that you should follow when you are scripting git: +This manual describes the convention used throughout git CLI. + +Many commands take revisions (most often "commits", but sometimes +"tree-ish", depending on the context and command) and paths as their +arguments. Here are the rules: + + * Revisions come first and then paths. + E.g. in `git diff v1.0 v2.0 arch/x86 include/asm-x86`, + `v1.0` and `v2.0` are revisions and `arch/x86` and `include/asm-x86` + are paths. + + * When an argument can be misunderstood as either a revision or a path, + they can be disambiguated by placing `\--` between them. + E.g. `git diff \-- HEAD` is, "I have a file called HEAD in my work + tree. Please show changes between the version I staged in the index + and what I have in the work tree for that file". not "show difference + between the HEAD commit and the work tree as a whole". You can say + `git diff HEAD \--` to ask for the latter. + + * Without disambiguating `\--`, git makes a reasonable guess, but errors + out and asking you to disambiguate when ambiguous. E.g. if you have a + file called HEAD in your work tree, `git diff HEAD` is ambiguous, and + you have to say either `git diff HEAD \--` or `git diff \-- HEAD` to + disambiguate. + +When writing a script that is expected to handle random user-input, it is +a good practice to make it explicit which arguments are which by placing +disambiguating `\--` at appropriate places. + +Here are the rules regarding the "flags" that you should follow when you are +scripting git: * it's preferred to use the non dashed form of git commands, which means that you should prefer `"git foo"` to `"git-foo"`. @@ -34,8 +63,8 @@ the rules that you should follow when you are scripting git: if you happen to have a file called `HEAD` in the work tree. -ENHANCED CLI ------------- +ENHANCED OPTION PARSER +---------------------- From the git 1.5.4 series and further, many git commands (not all of them at the time of the writing though) come with an enhanced option parser. diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt index e3d5c1fbf0..31e8a23a4f 100644 --- a/Documentation/gittutorial-2.txt +++ b/Documentation/gittutorial-2.txt @@ -61,9 +61,9 @@ from your own version. Note that you can shorten it to only a few characters to save yourself typing all 40 hex digits: ------------------------------------------------ -$ git-cat-file -t 54196cc2 +$ git cat-file -t 54196cc2 commit -$ git-cat-file commit 54196cc2 +$ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 @@ -166,7 +166,7 @@ hello world! and the "parent" object refers to the previous commit: ------------------------------------------------ -$ git-cat-file commit 54196cc2 +$ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 @@ -246,7 +246,7 @@ The last diff is empty, but no new commits have been made, and the head still doesn't contain the new line: ------------------------------------------------ -$ git-diff HEAD +$ git diff HEAD diff --git a/file.txt b/file.txt index a042389..513feba 100644 --- a/file.txt @@ -273,7 +273,7 @@ hello world, again So what our "git add" did was store a new blob and then put a reference to it in the index file. If we modify the file again, -we'll see that the new modifications are reflected in the "git-diff" +we'll see that the new modifications are reflected in the "git diff" output: ------------------------------------------------ diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index f221447478..cb7cd4b538 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -16,7 +16,7 @@ elif test -d .git -o -f .git && case "$VN" in *$LF*) (exit 1) ;; v[0-9]*) - test -z "$(git diff-index --name-only HEAD)" || + test -z "$(git diff-index --name-only HEAD --)" || VN="$VN-dirty" ;; esac then @@ -174,7 +174,7 @@ prefix = $(HOME) bindir = $(prefix)/bin mandir = $(prefix)/share/man infodir = $(prefix)/share/info -gitexecdir = $(bindir) +gitexecdir = $(prefix)/libexec/git-core sharedir = $(prefix)/share template_dir = $(sharedir)/git-core/templates htmldir=$(sharedir)/doc/git-doc @@ -378,6 +378,7 @@ LIB_H += unpack-trees.h LIB_H += utf8.h LIB_H += wt-status.h +LIB_OBJS += abspath.o LIB_OBJS += alias.o LIB_OBJS += alloc.o LIB_OBJS += archive.o @@ -1271,7 +1272,7 @@ 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) git$X '$(DESTDIR_SQ)$(bindir_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 @@ -1289,6 +1290,7 @@ endif ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) endif + ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X' install-doc: $(MAKE) -C Documentation install diff --git a/abspath.c b/abspath.c new file mode 100644 index 0000000000..4f95a954d5 --- /dev/null +++ b/abspath.c @@ -0,0 +1,68 @@ +#include "cache.h" + +/* We allow "recursive" symbolic links. Only within reason, though. */ +#define MAXDEPTH 5 + +const char *make_absolute_path(const char *path) +{ + static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; + char cwd[1024] = ""; + int buf_index = 1, len; + + int depth = MAXDEPTH; + char *last_elem = NULL; + struct stat st; + + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die ("Too long path: %.*s", 60, path); + + while (depth--) { + if (stat(buf, &st) || !S_ISDIR(st.st_mode)) { + char *last_slash = strrchr(buf, '/'); + if (last_slash) { + *last_slash = '\0'; + last_elem = xstrdup(last_slash + 1); + } else { + last_elem = xstrdup(buf); + *buf = '\0'; + } + } + + if (*buf) { + if (!*cwd && !getcwd(cwd, sizeof(cwd))) + die ("Could not get current working directory"); + + if (chdir(buf)) + die ("Could not switch to '%s'", buf); + } + if (!getcwd(buf, PATH_MAX)) + die ("Could not get current working directory"); + + if (last_elem) { + int len = strlen(buf); + if (len + strlen(last_elem) + 2 > PATH_MAX) + die ("Too long path name: '%s/%s'", + buf, last_elem); + buf[len] = '/'; + strcpy(buf + len + 1, last_elem); + free(last_elem); + last_elem = NULL; + } + + if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { + len = readlink(buf, next_buf, PATH_MAX); + if (len < 0) + die ("Invalid symlink: %s", buf); + next_buf[len] = '\0'; + buf = next_buf; + buf_index = 1 - buf_index; + next_buf = bufs[buf_index]; + } else + break; + } + + if (*cwd && chdir(cwd)) + die ("Could not change back to '%s'", cwd); + + return buf; +} diff --git a/builtin-apply.c b/builtin-apply.c index 92f00471bb..985ca3be5a 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,6 +12,7 @@ #include "blob.h" #include "delta.h" #include "builtin.h" +#include "path-list.h" /* * --check turns on checking that the working tree matches the @@ -185,6 +186,13 @@ struct image { struct line *line; }; +/* + * Records filenames that have been touched, in order to handle + * the case where more than one patches touch the same file. + */ + +static struct path_list fn_table; + static uint32_t hash_line(const char *cp, size_t len) { size_t i; @@ -2175,15 +2183,62 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) return 0; } +static struct patch *in_fn_table(const char *name) +{ + struct path_list_item *item; + + if (name == NULL) + return NULL; + + item = path_list_lookup(name, &fn_table); + if (item != NULL) + return (struct patch *)item->util; + + return NULL; +} + +static void add_to_fn_table(struct patch *patch) +{ + struct path_list_item *item; + + /* + * Always add new_name unless patch is a deletion + * This should cover the cases for normal diffs, + * file creations and copies + */ + if (patch->new_name != NULL) { + item = path_list_insert(patch->new_name, &fn_table); + item->util = patch; + } + + /* + * store a failure on rename/deletion cases because + * later chunks shouldn't patch old names + */ + if ((patch->new_name == NULL) || (patch->is_rename)) { + item = path_list_insert(patch->old_name, &fn_table); + item->util = (struct patch *) -1; + } +} + static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { struct strbuf buf; struct image image; size_t len; char *img; + struct patch *tpatch; strbuf_init(&buf, 0); - if (cached) { + + if ((tpatch = in_fn_table(patch->old_name)) != NULL) { + if (tpatch == (struct patch *) -1) { + return error("patch %s has been renamed/deleted", + patch->old_name); + } + /* We have a patched copy in memory use that */ + strbuf_add(&buf, tpatch->result, tpatch->resultsize); + } else if (cached) { if (read_file_or_gitlink(ce, &buf)) return error("read of %s failed", patch->old_name); } else if (patch->old_name) { @@ -2210,6 +2265,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * return -1; /* note with --reject this succeeds. */ patch->result = image.buf; patch->resultsize = image.len; + add_to_fn_table(patch); free(image.line_allocated); if (0 < patch->is_delete && patch->resultsize) @@ -2254,6 +2310,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) { const char *old_name = patch->old_name; + struct patch *tpatch; int stat_ret = 0; unsigned st_mode = 0; @@ -2267,12 +2324,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; assert(patch->is_new <= 0); - if (!cached) { + if ((tpatch = in_fn_table(old_name)) != NULL) { + if (tpatch == (struct patch *) -1) { + return error("%s: has been deleted/renamed", old_name); + } + st_mode = tpatch->new_mode; + } else if (!cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) return error("%s: %s", old_name, strerror(errno)); } - if (check_index) { + if (check_index && !tpatch) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) @@ -2324,7 +2386,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; } -static int check_patch(struct patch *patch, struct patch *prev_patch) +static int check_patch(struct patch *patch) { struct stat st; const char *old_name = patch->old_name; @@ -2341,8 +2403,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) return status; old_name = patch->old_name; - if (new_name && prev_patch && 0 < prev_patch->is_delete && - !strcmp(prev_patch->old_name, new_name)) + if (in_fn_table(new_name) == (struct patch *) -1) /* * A type-change diff is always split into a patch to * delete old, immediately followed by a patch to @@ -2392,15 +2453,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) static int check_patch_list(struct patch *patch) { - struct patch *prev_patch = NULL; int err = 0; - for (prev_patch = NULL; patch ; patch = patch->next) { + while (patch) { if (apply_verbosely) say_patch_name(stderr, "Checking patch ", patch, "...\n"); - err |= check_patch(patch, prev_patch); - prev_patch = patch; + err |= check_patch(patch); + patch = patch->next; } return err; } @@ -2918,6 +2978,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) struct patch *list = NULL, **listp = &list; int skipped_patch = 0; + /* FIXME - memory leak when using multiple patch files as inputs */ + memset(&fn_table, 0, sizeof(struct path_list)); strbuf_init(&buf, 0); patch_input_file = filename; read_patch_file(&buf, fd); diff --git a/builtin-cat-file.c b/builtin-cat-file.c index bd343efae7..880e75af5e 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -181,6 +181,7 @@ static int batch_one_object(const char *obj_name, int print_contents) write_or_die(1, contents, size); printf("\n"); fflush(stdout); + free(contents); } return 0; diff --git a/builtin-clone.c b/builtin-clone.c index f13845fb7f..643c7d4169 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -424,6 +424,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) fprintf(stderr, "Initialize %s\n", git_dir); init_db(option_template, option_quiet ? INIT_DB_QUIET : 0); + /* + * At this point, the config exists, so we do not need the + * environment variable. We actually need to unset it, too, to + * re-enable parsing of the global configs. + */ + unsetenv(CONFIG_ENVIRONMENT); + if (option_reference) setup_reference(git_dir); @@ -456,7 +463,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) refs = clone_local(path, git_dir); else { struct remote *remote = remote_get(argv[0]); - struct transport *transport = transport_get(remote, argv[0]); + struct transport *transport = + transport_get(remote, remote->url[0]); if (!transport->get_refs_list || !transport->fetch) die("Don't know how to clone %s", transport->url); diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index e5e4bdbe86..3881f6c2f5 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -24,26 +24,20 @@ static void check_valid(unsigned char *sha1, enum object_type expect) typename(expect)); } -/* - * Having more than two parents is not strange at all, and this is - * how multi-way merges are represented. - */ -#define MAXPARENT (16) -static unsigned char parent_sha1[MAXPARENT][20]; - static const char commit_tree_usage[] = "git-commit-tree <sha1> [-p <sha1>]* < changelog"; -static int new_parent(int idx) +static void new_parent(struct commit *parent, struct commit_list **parents_p) { - int i; - unsigned char *sha1 = parent_sha1[idx]; - for (i = 0; i < idx; i++) { - if (!hashcmp(parent_sha1[i], sha1)) { + unsigned char *sha1 = parent->object.sha1; + struct commit_list *parents; + for (parents = *parents_p; parents; parents = parents->next) { + if (parents->item == parent) { error("duplicate parent %s ignored", sha1_to_hex(sha1)); - return 0; + return; } + parents_p = &parents->next; } - return 1; + commit_list_insert(parent, parents_p); } static const char commit_utf8_warn[] = @@ -54,7 +48,7 @@ static const char commit_utf8_warn[] = int cmd_commit_tree(int argc, const char **argv, const char *prefix) { int i; - int parents = 0; + struct commit_list *parents = NULL; unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; struct strbuf buffer; @@ -69,18 +63,16 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) check_valid(tree_sha1, OBJ_TREE); for (i = 2; i < argc; i += 2) { + unsigned char sha1[20]; const char *a, *b; a = argv[i]; b = argv[i+1]; if (!b || strcmp(a, "-p")) usage(commit_tree_usage); - if (parents >= MAXPARENT) - die("Too many parents (%d max)", MAXPARENT); - if (get_sha1(b, parent_sha1[parents])) + if (get_sha1(b, sha1)) die("Not a valid object name %s", b); - check_valid(parent_sha1[parents], OBJ_COMMIT); - if (new_parent(parents)) - parents++; + check_valid(sha1, OBJ_COMMIT); + new_parent(lookup_commit(sha1), &parents); } /* Not having i18n.commitencoding is the same as having utf-8 */ @@ -94,8 +86,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) * different order of parents will be a _different_ changeset even * if everything else stays the same. */ - for (i = 0; i < parents; i++) - strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i])); + while (parents) { + struct commit_list *next = parents->next; + strbuf_addf(&buffer, "parent %s\n", + sha1_to_hex(parents->item->object.sha1)); + free(parents); + parents = next; + } /* Person/date information */ strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME)); diff --git a/builtin-fetch.c b/builtin-fetch.c index e81ee2d02b..97fdc51e31 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -181,9 +181,9 @@ static int s_update_ref(const char *action, lock = lock_any_ref_for_update(ref->name, check_old ? ref->old_sha1 : NULL, 0); if (!lock) - return 1; + return 2; if (write_ref_sha1(lock, ref->new_sha1, msg) < 0) - return 1; + return 2; return 0; } @@ -233,10 +233,12 @@ static int update_local_ref(struct ref *ref, if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - sprintf(display, "- %-*s %-*s -> %s", + int r; + r = s_update_ref("updating tag", ref, 0); + sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-', SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, - pretty_ref); - return s_update_ref("updating tag", ref, 0); + pretty_ref, r ? " (unable to update local ref)" : ""); + return r; } current = lookup_commit_reference_gently(ref->old_sha1, 1); @@ -244,6 +246,7 @@ static int update_local_ref(struct ref *ref, if (!current || !updated) { const char *msg; const char *what; + int r; if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; what = "[new tag]"; @@ -253,27 +256,36 @@ static int update_local_ref(struct ref *ref, what = "[new branch]"; } - sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what, - REFCOL_WIDTH, remote, pretty_ref); - return s_update_ref(msg, ref, 0); + r = s_update_ref(msg, ref, 0); + sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*', + SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref, + r ? " (unable to update local ref)" : ""); + return r; } if (in_merge_bases(current, &updated, 1)) { char quickref[83]; + int r; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, ".."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref, - REFCOL_WIDTH, remote, pretty_ref); - return s_update_ref("fast forward", ref, 1); + r = s_update_ref("fast forward", ref, 1); + sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ', + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, + pretty_ref, r ? " (unable to update local ref)" : ""); + return r; } else if (force || ref->force) { char quickref[84]; + int r; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, "..."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, "+ %-*s %-*s -> %s (forced update)", - SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref); - return s_update_ref("forced-update", ref, 1); + r = s_update_ref("forced-update", ref, 1); + sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+', + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, + pretty_ref, + r ? "unable to update local ref" : "forced update"); + return r; } else { sprintf(display, "! %-*s %-*s -> %s (non fast forward)", SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, @@ -282,7 +294,8 @@ static int update_local_ref(struct ref *ref, } } -static int store_updated_refs(const char *url, struct ref *ref_map) +static int store_updated_refs(const char *url, const char *remote_name, + struct ref *ref_map) { FILE *fp; struct commit *commit; @@ -368,6 +381,10 @@ static int store_updated_refs(const char *url, struct ref *ref_map) } } fclose(fp); + if (rc & 2) + error("some local refs could not be updated; try running\n" + " 'git remote prune %s' to remove any old, conflicting " + "branches", remote_name); return rc; } @@ -438,7 +455,9 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) if (ret) ret = transport_fetch_refs(transport, ref_map); if (!ret) - ret |= store_updated_refs(transport->url, ref_map); + ret |= store_updated_refs(transport->url, + transport->remote->name, + ref_map); transport_unlock_pack(transport); return ret; } diff --git a/builtin-reset.c b/builtin-reset.c index f34acb1915..a0321694c5 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -194,8 +194,40 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); - if (i < argc && strcmp(argv[i], "--")) - rev = argv[i++]; + /* + * Possible arguments are: + * + * git reset [-opts] <rev> <paths>... + * git reset [-opts] <rev> -- <paths>... + * git reset [-opts] -- <paths>... + * git reset [-opts] <paths>... + * + * At this point, argv[i] points immediately after [-opts]. + */ + + if (i < argc) { + if (!strcmp(argv[i], "--")) { + i++; /* reset to HEAD, possibly with paths */ + } else if (i + 1 < argc && !strcmp(argv[i+1], "--")) { + rev = argv[i]; + i += 2; + } + /* + * Otherwise, argv[i] could be either <rev> or <paths> and + * has to be unambigous. + */ + else if (!get_sha1(argv[i], sha1)) { + /* + * Ok, argv[i] looks like a rev; it should not + * be a filename. + */ + verify_non_filename(prefix, argv[i]); + rev = argv[i++]; + } else { + /* Otherwise we treat this as a filename */ + verify_filename(prefix, argv[i]); + } + } if (get_sha1(rev, sha1)) die("Failed to resolve '%s' as a valid ref.", rev); @@ -205,9 +237,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die("Could not parse object '%s'.", rev); hashcpy(sha1, commit->object.sha1); - if (i < argc && !strcmp(argv[i], "--")) - i++; - /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 48ae09e9b5..371400d49a 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -30,7 +30,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) if (argc != 2) usage(upload_archive_usage); - if (strlen(argv[1]) > sizeof(buf)) + if (strlen(argv[1]) + 1 > sizeof(buf)) die("insanely long repository name"); strcpy(buf, argv[1]); /* enter-repo smudges its argument */ diff --git a/check_bindir b/check_bindir new file mode 100755 index 0000000000..a1c4c3e8d8 --- /dev/null +++ b/check_bindir @@ -0,0 +1,13 @@ +#!/bin/sh +bindir="$1" +gitexecdir="$2" +gitcmd="$3" +if test "$bindir" != "$gitexecdir" -a -x "$gitcmd" +then + echo + echo "!! You have installed git-* commands to new gitexecdir." + echo "!! Old version git-* commands still remain in bindir." + echo "!! Mixing two versions of Git will lead to problems." + echo "!! Please remove old version commands in bindir now." + echo +fi diff --git a/config.mak.in b/config.mak.in index 7868dfd93a..b776149531 100644 --- a/config.mak.in +++ b/config.mak.in @@ -11,7 +11,7 @@ TCLTK_PATH = @TCLTK_PATH@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ -#gitexecdir = @libexecdir@/git-core/ +gitexecdir = @libexecdir@/git-core/ datarootdir = @datarootdir@ template_dir = @datadir@/git-core/templates/ diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ebf7cde5c0..3f46149853 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1041,7 +1041,6 @@ _git_config () pull.octopus pull.twohead repack.useDeltaBaseOffset - show.difftree showbranch.default tar.umask transfer.unpackLimit @@ -1050,7 +1049,6 @@ _git_config () user.name user.email user.signingkey - whatchanged.difftree branch. remote. " } @@ -830,12 +830,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) /* Sanity: give at least 5 columns to the graph, * but leave at least 10 columns for the name. */ - if (width < name_width + 15) { - if (name_width <= 25) - width = name_width + 15; - else - name_width = width - 15; - } + if (width < 25) + width = 25; + if (name_width < 10) + name_width = 10; + else if (width < name_width + 15) + name_width = width - 15; /* Find the longest filename and max number of changes */ reset = diff_get_color_opt(options, DIFF_RESET); diff --git a/environment.c b/environment.c index 084ac8a436..4a88a17d54 100644 --- a/environment.c +++ b/environment.c @@ -13,7 +13,6 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int user_ident_explicitly_given; int trust_executable_bit = 1; -int quote_path_fully = 1; int has_symlinks = 1; int ignore_case; int assume_unchanged; diff --git a/exec_cmd.c b/exec_cmd.c index e189caca62..0f8f4b5b7d 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -65,32 +65,25 @@ void setup_path(const char *cmd_path) int execv_git_cmd(const char **argv) { - struct strbuf cmd; - const char *tmp; - - strbuf_init(&cmd, 0); - strbuf_addf(&cmd, "git-%s", argv[0]); + int argc; + const char **nargv; - /* - * argv[0] must be the git command, but the argv array - * belongs to the caller, and may be reused in - * subsequent loop iterations. Save argv[0] and - * restore it on error. - */ - tmp = argv[0]; - argv[0] = cmd.buf; + for (argc = 0; argv[argc]; argc++) + ; /* just counting */ + nargv = xmalloc(sizeof(*nargv) * (argc + 2)); - trace_argv_printf(argv, "trace: exec:"); + nargv[0] = "git"; + for (argc = 0; argv[argc]; argc++) + nargv[argc + 1] = argv[argc]; + nargv[argc + 1] = NULL; + trace_argv_printf(nargv, "trace: exec:"); /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); + execvp("git", (char **)nargv); trace_printf("trace: exec failed: %s\n", strerror(errno)); - argv[0] = tmp; - - strbuf_release(&cmd); - + free(nargv); return -1; } diff --git a/git-repack.sh b/git-repack.sh index 072d1b40f7..8c3bc134ad 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -44,11 +44,7 @@ do shift done -# Later we will default repack.UseDeltaBaseOffset to true -default_dbo=false - -case "`git config --bool repack.usedeltabaseoffset || - echo $default_dbo`" in +case "`git config --bool repack.usedeltabaseoffset || echo true`" in true) extra="$extra --delta-base-offset" ;; esac diff --git a/git-send-email.perl b/git-send-email.perl index 0b04ba32f0..a047b016e3 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -84,7 +84,10 @@ Options: --smtp-pass The password for SMTP-AUTH. - --smtp-ssl If set, connects to the SMTP server using SSL. + --smtp-encryption Specify 'tls' for STARTTLS encryption, or 'ssl' for SSL. + Any other value disables the feature. + + --smtp-ssl Synonym for '--smtp-encryption=ssl'. Deprecated. --suppress-cc Suppress the specified category of auto-CC. The category can be one of 'author' for the patch author, 'self' to @@ -184,7 +187,7 @@ my ($quiet, $dry_run) = (0, 0); # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd); -my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_ssl); +my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); my ($no_validate); my (@suppress_cc); @@ -194,7 +197,6 @@ my %config_bool_settings = ( "chainreplyto" => [\$chain_reply_to, 1], "suppressfrom" => [\$suppress_from, undef], "signedoffcc" => [\$signed_off_cc, undef], - "smtpssl" => [\$smtp_ssl, 0], ); my %config_settings = ( @@ -249,7 +251,8 @@ my $rc = GetOptions("sender|from=s" => \$sender, "smtp-server-port=s" => \$smtp_server_port, "smtp-user=s" => \$smtp_authuser, "smtp-pass:s" => \$smtp_authpass, - "smtp-ssl!" => \$smtp_ssl, + "smtp-ssl" => sub { $smtp_encryption = 'ssl' }, + "smtp-encryption=s" => \$smtp_encryption, "identity=s" => \$identity, "compose" => \$compose, "quiet" => \$quiet, @@ -289,6 +292,15 @@ sub read_config { $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target); } } + + if (!defined $smtp_encryption) { + my $enc = Git::config(@repo, "$prefix.smtpencryption"); + if (defined $enc) { + $smtp_encryption = $enc; + } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) { + $smtp_encryption = 'ssl'; + } + } } # read configuration from [sendemail "$identity"], fall back on [sendemail] @@ -301,6 +313,9 @@ foreach my $setting (values %config_bool_settings) { ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]})); } +# 'default' encryption is none -- this only prevents a warning +$smtp_encryption = '' unless (defined $smtp_encryption); + # Set CC suppressions my(%suppress_cc); if (@suppress_cc) { @@ -393,7 +408,7 @@ for my $f (@ARGV) { push @files, grep { -f $_ } map { +$f . "/" . $_ } sort readdir(DH); - } elsif (-f $f) { + } elsif (-f $f or -p $f) { push @files, $f; } else { @@ -403,8 +418,10 @@ for my $f (@ARGV) { if (!$no_validate) { foreach my $f (@files) { - my $error = validate_patch($f); - $error and die "fatal: $f: $error\nwarning: no patches were sent\n"; + unless (-p $f) { + my $error = validate_patch($f); + $error and die "fatal: $f: $error\nwarning: no patches were sent\n"; + } } } @@ -738,7 +755,7 @@ X-Mailer: git-send-email $gitversion die "The required SMTP server is not properly defined." } - if ($smtp_ssl) { + if ($smtp_encryption eq 'ssl') { $smtp_server_port ||= 465; # ssmtp require Net::SMTP::SSL; $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port); @@ -748,6 +765,17 @@ X-Mailer: git-send-email $gitversion $smtp ||= Net::SMTP->new((defined $smtp_server_port) ? "$smtp_server:$smtp_server_port" : $smtp_server); + if ($smtp_encryption eq 'tls') { + require Net::SMTP::SSL; + $smtp->command('STARTTLS'); + $smtp->response(); + if ($smtp->code == 220) { + $smtp = Net::SMTP::SSL->start_SSL($smtp) + or die "STARTTLS failed! ".$smtp->message; + } else { + die "Server does not support STARTTLS! ".$smtp->message; + } + } } if (!$smtp) { diff --git a/git-svn.perl b/git-svn.perl index 4c9c59bc3f..f789a6eeca 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1462,13 +1462,6 @@ sub verify_remotes_sanity { } } -# we allow more chars than remotes2config.sh... -sub sanitize_remote_name { - my ($name) = @_; - $name =~ tr{A-Za-z0-9:,/+-}{.}c; - $name; -} - sub find_existing_remote { my ($url, $remotes) = @_; return undef if $no_reuse_existing; @@ -2853,7 +2846,7 @@ sub _new { unless (defined $ref_id && length $ref_id) { $_[2] = $ref_id = $Git::SVN::default_ref_id; } - $_[1] = $repo_id = sanitize_remote_name($repo_id); + $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; $_[3] = $path = '' unless (defined $path); mkpath(["$ENV{GIT_DIR}/svn"]); @@ -3243,7 +3236,9 @@ sub close_file { my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1); my $result; while ($result = sysread($fh, my $string, 1024)) { - syswrite($tmp_fh, $string, $result); + my $wrote = syswrite($tmp_fh, $string, $result); + defined($wrote) && $wrote == $result + or croak("write $tmp_filename: $!\n"); } defined $result or croak $!; close $tmp_fh or croak $!; @@ -3251,6 +3246,7 @@ sub close_file { close $fh or croak $!; $hash = $::_repository->hash_and_insert_object($tmp_filename); + unlink($tmp_filename); $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n"; close $fb->{base} or croak $!; } else { @@ -4704,8 +4700,7 @@ sub minimize_connections { # skip existing cases where we already connect to the root if (($ra->{url} eq $ra->{repos_root}) || - (Git::SVN::sanitize_remote_name($ra->{repos_root}) eq - $repo_id)) { + ($ra->{repos_root} eq $repo_id)) { $root_repos->{$ra->{url}} = $repo_id; next; } @@ -4744,8 +4739,7 @@ sub minimize_connections { foreach my $url (keys %$new_urls) { # see if we can re-use an existing [svn-remote "repo_id"] # instead of creating a(n ugly) new section: - my $repo_id = $root_repos->{$url} || - Git::SVN::sanitize_remote_name($url); + my $repo_id = $root_repos->{$url} || $url; my $fetch = $new_urls->{$url}; foreach my $path (keys %$fetch) { @@ -384,6 +384,36 @@ static void handle_internal_command(int argc, const char **argv) } } +static void execv_dashed_external(const char **argv) +{ + struct strbuf cmd; + const char *tmp; + + strbuf_init(&cmd, 0); + strbuf_addf(&cmd, "git-%s", argv[0]); + + /* + * argv[0] must be the git command, but the argv array + * belongs to the caller, and may be reused in + * subsequent loop iterations. Save argv[0] and + * restore it on error. + */ + tmp = argv[0]; + argv[0] = cmd.buf; + + trace_argv_printf(argv, "trace: exec:"); + + /* execvp() can only ever return if it fails */ + execvp(cmd.buf, (char **)argv); + + trace_printf("trace: exec failed: %s\n", strerror(errno)); + + argv[0] = tmp; + + strbuf_release(&cmd); +} + + int main(int argc, const char **argv) { const char *cmd = argv[0] ? argv[0] : "git-help"; @@ -448,7 +478,7 @@ int main(int argc, const char **argv) handle_internal_command(argc, argv); /* .. then try the external ones */ - execv_git_cmd(argv); + execv_dashed_external(argv); /* It could be an alias -- this works around the insanity * of overriding "git log" with "git show" by having diff --git a/git.spec.in b/git.spec.in index 3d7f3ef4af..c6492e5be2 100644 --- a/git.spec.in +++ b/git.spec.in @@ -117,6 +117,7 @@ find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';' find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';' (find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files +(find $RPM_BUILD_ROOT%{_libexecdir}/git-core -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) >> bin-man-doc-files (find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files %if %{!?_without_docs:1}0 (find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files @@ -136,7 +137,7 @@ rm -rf $RPM_BUILD_ROOT %files svn %defattr(-,root,root) -%{_bindir}/*svn* +%{_libexecdir}/git-core/*svn* %doc Documentation/*svn*.txt %{!?_without_docs: %{_mandir}/man1/*svn*.1*} %{!?_without_docs: %doc Documentation/*svn*.html } @@ -144,28 +145,28 @@ rm -rf $RPM_BUILD_ROOT %files cvs %defattr(-,root,root) %doc Documentation/*git-cvs*.txt -%{_bindir}/*cvs* +%{_libexecdir}/git-core/*cvs* %{!?_without_docs: %{_mandir}/man1/*cvs*.1*} %{!?_without_docs: %doc Documentation/*git-cvs*.html } %files arch %defattr(-,root,root) %doc Documentation/git-archimport.txt -%{_bindir}/git-archimport +%{_libexecdir}/git-core/git-archimport %{!?_without_docs: %{_mandir}/man1/git-archimport.1*} %{!?_without_docs: %doc Documentation/git-archimport.html } %files email %defattr(-,root,root) %doc Documentation/*email*.txt -%{_bindir}/*email* +%{_libexecdir}/git-core/*email* %{!?_without_docs: %{_mandir}/man1/*email*.1*} %{!?_without_docs: %doc Documentation/*email*.html } %files gui %defattr(-,root,root) -%{_bindir}/git-gui -%{_bindir}/git-citool +%{_libexecdir}/git-core/git-gui +%{_libexecdir}/git-core/git-citool %{_datadir}/git-gui/ %{!?_without_docs: %{_mandir}/man1/git-gui.1*} %{!?_without_docs: %doc Documentation/git-gui.html} @@ -527,20 +527,26 @@ static int is_git_command(const char *s) is_in_cmdlist(&other_cmds, s); } +static const char *prepend(const char *prefix, const char *cmd) +{ + size_t pre_len = strlen(prefix); + size_t cmd_len = strlen(cmd); + char *p = xmalloc(pre_len + cmd_len + 1); + memcpy(p, prefix, pre_len); + strcpy(p + pre_len, cmd); + return p; +} + static const char *cmd_to_page(const char *git_cmd) { if (!git_cmd) return "git"; else if (!prefixcmp(git_cmd, "git")) return git_cmd; - else { - int page_len = strlen(git_cmd) + 4; - char *p = xmalloc(page_len + 1); - strcpy(p, "git-"); - strcpy(p + 4, git_cmd); - p[page_len] = 0; - return p; - } + else if (is_git_command(git_cmd)) + return prepend("git-", git_cmd); + else + return prepend("git", git_cmd); } static void setup_man_path(void) diff --git a/pack-write.c b/pack-write.c index f52cabe838..a8f0269936 100644 --- a/pack-write.c +++ b/pack-write.c @@ -2,7 +2,7 @@ #include "pack.h" #include "csum-file.h" -uint32_t pack_idx_default_version = 1; +uint32_t pack_idx_default_version = 2; uint32_t pack_idx_off32_limit = 0x7fffffff; static int sha1_compare(const void *_a, const void *_b) @@ -327,9 +327,6 @@ const char *make_nonrelative_path(const char *path) return buf; } -/* We allow "recursive" symbolic links. Only within reason, though. */ -#define MAXDEPTH 5 - const char *make_relative_path(const char *abs, const char *base) { static char buf[PATH_MAX + 1]; @@ -346,67 +343,3 @@ const char *make_relative_path(const char *abs, const char *base) strcpy(buf, abs + baselen); return buf; } - -const char *make_absolute_path(const char *path) -{ - static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; - char cwd[1024] = ""; - int buf_index = 1, len; - - int depth = MAXDEPTH; - char *last_elem = NULL; - struct stat st; - - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); - - while (depth--) { - if (stat(buf, &st) || !S_ISDIR(st.st_mode)) { - char *last_slash = strrchr(buf, '/'); - if (last_slash) { - *last_slash = '\0'; - last_elem = xstrdup(last_slash + 1); - } else { - last_elem = xstrdup(buf); - *buf = '\0'; - } - } - - if (*buf) { - if (!*cwd && !getcwd(cwd, sizeof(cwd))) - die ("Could not get current working directory"); - - if (chdir(buf)) - die ("Could not switch to '%s'", buf); - } - if (!getcwd(buf, PATH_MAX)) - die ("Could not get current working directory"); - - if (last_elem) { - int len = strlen(buf); - if (len + strlen(last_elem) + 2 > PATH_MAX) - die ("Too long path name: '%s/%s'", - buf, last_elem); - buf[len] = '/'; - strcpy(buf + len + 1, last_elem); - free(last_elem); - last_elem = NULL; - } - - if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { - len = readlink(buf, next_buf, PATH_MAX); - if (len < 0) - die ("Invalid symlink: %s", buf); - next_buf[len] = '\0'; - buf = next_buf; - buf_index = 1 - buf_index; - next_buf = bufs[buf_index]; - } else - break; - } - - if (*cwd && chdir(cwd)) - die ("Could not change back to '%s'", cwd); - - return buf; -} @@ -1,6 +1,8 @@ #include "cache.h" #include "quote.h" +int quote_path_fully = 1; + /* Help to copy the thing properly quoted for the shell safety. * any single quote is replaced with '\'', any exclamation point * is replaced with '\!', and the whole thing is enclosed in a @@ -3,6 +3,14 @@ #include "exec_cmd.h" #include "strbuf.h" +/* Stubs for functions that make no sense for git-shell. These stubs + * are provided here to avoid linking in external redundant modules. + */ +void release_pack_memory(size_t need, int fd){} +void trace_argv_printf(const char **argv, const char *fmt, ...){} +void trace_printf(const char *fmt, ...){} + + static int do_generic_cmd(const char *me, char *arg) { const char *my_argv[4]; diff --git a/t/.gitignore b/t/.gitignore index 11ffd910c1..b27e280083 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1 +1,2 @@ /trash directory +/test-results diff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh new file mode 100755 index 0000000000..2a6ed77c65 --- /dev/null +++ b/t/t4127-apply-same-fn.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +test_description='apply same filename' + +. ./test-lib.sh + +test_expect_success setup ' + for i in a b c d e f g h i j k l m + do + echo $i + done >same_fn && + cp same_fn other_fn && + git add same_fn other_fn && + git commit -m initial +' +test_expect_success 'apply same filename with independent changes' ' + sed -i -e "s/^d/z/" same_fn && + git diff > patch0 && + git add same_fn && + sed -i -e "s/^i/y/" same_fn && + git diff >> patch0 && + cp same_fn same_fn2 && + git reset --hard && + git-apply patch0 && + diff same_fn same_fn2 +' + +test_expect_success 'apply same filename with overlapping changes' ' + git reset --hard + sed -i -e "s/^d/z/" same_fn && + git diff > patch0 && + git add same_fn && + sed -i -e "s/^e/y/" same_fn && + git diff >> patch0 && + cp same_fn same_fn2 && + git reset --hard && + git-apply patch0 && + diff same_fn same_fn2 +' + +test_expect_success 'apply same new filename after rename' ' + git reset --hard + git mv same_fn new_fn + sed -i -e "s/^d/z/" new_fn && + git add new_fn && + git diff -M --cached > patch1 && + sed -i -e "s/^e/y/" new_fn && + git diff >> patch1 && + cp new_fn new_fn2 && + git reset --hard && + git apply --index patch1 && + diff new_fn new_fn2 +' + +test_expect_success 'apply same old filename after rename -- should fail.' ' + git reset --hard + git mv same_fn new_fn + sed -i -e "s/^d/z/" new_fn && + git add new_fn && + git diff -M --cached > patch1 && + git mv new_fn same_fn + sed -i -e "s/^e/y/" same_fn && + git diff >> patch1 && + git reset --hard && + test_must_fail git apply patch1 +' + +test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' ' + git reset --hard + git mv same_fn new_fn + sed -i -e "s/^d/z/" new_fn && + git add new_fn && + git diff -M --cached > patch1 && + git commit -m "a rename" && + git mv other_fn same_fn + sed -i -e "s/^e/y/" same_fn && + git add same_fn && + git diff -M --cached >> patch1 && + sed -i -e "s/^g/x/" same_fn && + git diff >> patch1 && + git reset --hard HEAD^ && + git apply patch1 +' + +test_done diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 39ba14148c..96d15083fb 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -428,4 +428,51 @@ test_expect_success '--mixed refreshes the index' ' test_cmp expect output ' +test_expect_success 'disambiguation (1)' ' + + git reset --hard && + >secondfile && + git add secondfile && + test_must_fail git reset secondfile && + test -z "$(git diff --cached --name-only)" && + test -f secondfile && + test ! -s secondfile + +' + +test_expect_success 'disambiguation (2)' ' + + git reset --hard && + >secondfile && + git add secondfile && + rm -f secondfile && + test_must_fail git reset secondfile && + test -n "$(git diff --cached --name-only -- secondfile)" && + test ! -f secondfile + +' + +test_expect_success 'disambiguation (3)' ' + + git reset --hard && + >secondfile && + git add secondfile && + rm -f secondfile && + test_must_fail git reset HEAD secondfile && + test -z "$(git diff --cached --name-only)" && + test ! -f secondfile + +' + +test_expect_success 'disambiguation (4)' ' + + git reset --hard && + >secondfile && + git add secondfile && + rm -f secondfile && + test_must_fail git reset -- secondfile && + test -z "$(git diff --cached --name-only)" && + test ! -f secondfile +' + test_done diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index b2fb9ece9c..9706ee5773 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -6,6 +6,11 @@ test_description='perl interface (Git.pm)' . ./test-lib.sh +perl -MTest::More -e 0 2>/dev/null || { + say_color skip "Perl Test::More unavailable, skipping test" + test_done +} + # set up test repository test_expect_success \ diff --git a/t/test-lib.sh b/t/test-lib.sh index 4010110010..c0c5e0e83b 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -407,7 +407,7 @@ test_create_repo () { repo="$1" mkdir "$repo" cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >/dev/null 2>&1 || + "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 || error "cannot run git init -- have you built things yet?" mv .git/hooks .git/hooks-disabled cd "$owd" |