diff options
101 files changed, 1327 insertions, 988 deletions
diff --git a/.gitignore b/.gitignore index 13311f1d5e..1c57d4c958 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,6 @@ git-mktag git-mktree git-name-rev git-mv -git-notes git-pack-redundant git-pack-objects git-pack-refs diff --git a/Documentation/RelNotes-1.6.1.4.txt b/Documentation/RelNotes-1.6.1.4.txt new file mode 100644 index 0000000000..a9f1a6b8b5 --- /dev/null +++ b/Documentation/RelNotes-1.6.1.4.txt @@ -0,0 +1,19 @@ +GIT v1.6.1.4 Release Notes +========================== + +Fixes since v1.6.1.3 +-------------------- + +* "git fast-export" produced wrong output with some parents missing from + commits, when the history is clock-skewed. + +* "git fast-import" sometimes failed to read back objects it just wrote + out and aborted, because it failed to flush stale cached data. + +* "git repack" did not error out when necessary object was missing in the + repository. + +Also includes minor documentation fixes and updates. + +-- +git shortlog --no-merges v1.6.1.3.. diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt index f1bfa82749..6ed31595ba 100644 --- a/Documentation/RelNotes-1.6.2.txt +++ b/Documentation/RelNotes-1.6.2.txt @@ -25,8 +25,9 @@ Updates since v1.6.1 * gitweb updates, including a new patch view and RSS/Atom feed improvements. -* (contrib) git.el updates for better XEmacs compatibility; vc-git.el - is not shiped with git anymore (it is part of official Emacs) +* (contrib/emacs) git.el now has commands for checking out a branch, + creating a branch, cherry-picking and reverting commits; vc-git.el + is not shipped with git anymore (it is part of official Emacs). (performance) @@ -37,8 +38,6 @@ Updates since v1.6.1 * automatic typo correction works on aliases as well -* Initial support for "git notes" implemented. - * @{-1} is a way to refer to the last branch you were on. This is accepted not only where an object name is expected, but anywhere a branch name is expected. E.g. "git branch --track mybranch @{-1}" diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index ba07c8c571..9b559adefc 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -376,9 +376,36 @@ Thunderbird (A Large Angry SCM) +By default, Thunderbird will both wrap emails as well as flag them as +being 'format=flowed', both of which will make the resulting email unusable +by git. + Here are some hints on how to successfully submit patches inline using Thunderbird. +There are two different approaches. One approach is to configure +Thunderbird to not mangle patches. The second approach is to use +an external editor to keep Thunderbird from mangling the patches. + +Approach #1 (configuration): + +This recipe is current as of Thunderbird 2.0.0.19. Three steps: + 1. Configure your mail server composition as plain text + Edit...Account Settings...Composition & Addressing, + uncheck 'Compose Messages in HTML'. + 2. Configure your general composition window to not wrap + Edit..Preferences..Composition, wrap plain text messages at 0 + 3. Disable the use of format=flowed + Edit..Preferences..Advanced..Config Editor. Search for: + mailnews.send_plaintext_flowed + toggle it to make sure it is set to 'false'. + +After that is done, you should be able to compose email as you +otherwise would (cut + paste, git-format-patch | git-imap-send, etc), +and the patches should not be mangled. + +Approach #2 (external editor): + This recipe appears to work with the current [*1*] Thunderbird from Suse. The following Thunderbird extensions are needed: diff --git a/Documentation/config.txt b/Documentation/config.txt index 1dd18c928d..1806a60610 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -422,19 +422,6 @@ relatively high IO latencies. With this set to 'true', git will do the index comparison to the filesystem data in parallel, allowing overlapping IO's. -core.notesRef:: - When showing commit messages, also show notes which are stored in - the given ref. This ref is expected to contain files named - after the full SHA-1 of the commit they annotate. -+ -If such a file exists in the given ref, the referenced blob is read, and -appended to the commit message, separated by a "Notes:" line. If the -given ref itself does not exist, it is not an error, but means that no -notes should be printed. -+ -This setting defaults to "refs/notes/commits", and can be overridden by -the `GIT_NOTES_REF` environment variable. - alias.*:: Command aliases for the linkgit:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index ea0f6a0f3a..57590b1480 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -107,17 +107,17 @@ incremental bundle, ---------------- machineA$ cd R1 -machineA$ git bundle create file.bdl master +machineA$ git bundle create file.bundle master machineA$ git tag -f lastR2bundle master ---------------- -Then you sneakernet file.bdl to the target machine B. Because you don't +Then you sneakernet file.bundle to the target machine B. Because you don't have to have any object to extract objects from such a bundle, not only you can fetch/pull from a bundle, you can clone from it as if it was a remote repository. ---------------- -machineB$ git clone /home/me/tmp/file.bdl R2 +machineB$ git clone /home/me/tmp/file.bundle R2 ---------------- This will define a remote called "origin" in the resulting repository that @@ -126,12 +126,12 @@ have an entry like this: ------------------------ [remote "origin"] - url = /home/me/tmp/file.bdl + url = /home/me/tmp/file.bundle fetch = refs/heads/*:refs/remotes/origin/* ------------------------ You can fetch/pull to update the resulting mine.git repository after -replacing the bundle you store at /home/me/tmp/file.bdl with incremental +replacing the bundle you store at /home/me/tmp/file.bundle with incremental updates from here on. After working more in the original repository, you can create an @@ -139,11 +139,11 @@ incremental bundle to update the other: ---------------- machineA$ cd R1 -machineA$ git bundle create file.bdl lastR2bundle..master +machineA$ git bundle create file.bundle lastR2bundle..master machineA$ git tag -f lastR2bundle master ---------------- -and sneakernet it to the other machine to replace /home/me/tmp/file.bdl, +and sneakernet it to the other machine to replace /home/me/tmp/file.bundle, and pull from it. ---------------- diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 451950bab6..7ffe03f427 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -212,6 +212,11 @@ git filter-branch --index-filter 'git rm --cached filename' HEAD Now, you will get the rewritten history saved in HEAD. +As with using `rm filename`, `git rm --cached filename` will fail +if the file is absent from the tree of a commit. If it is not important +whether the file is already absent from the tree, you can use +`git rm --cached --ignore-unmatch filename` instead. + To rewrite the repository to look as if `foodir/` had been its project root, and discard all other history: @@ -334,6 +339,47 @@ git filter-branch --index-filter \ --------------------------------------------------------------- + +Checklist for Shrinking a Repository +------------------------------------ + +git-filter-branch is often used to get rid of a subset of files, +usually with some combination of `\--index-filter` and +`\--subdirectory-filter`. People expect the resulting repository to +be smaller than the original, but you need a few more steps to +actually make it smaller, because git tries hard not to lose your +objects until you tell it to. First make sure that: + +* You really removed all variants of a filename, if a blob was moved + over its lifetime. `git log \--name-only \--follow \--all \-- + filename` can help you find renames. + +* You really filtered all refs: use `\--tag-name-filter cat \-- + \--all` when calling git-filter-branch. + +Then there are two ways to get a smaller repository. A safer way is +to clone, that keeps your original intact. + +* Clone it with `git clone +++file:///path/to/repo+++`. The clone + will not have the removed objects. See linkgit:git-clone[1]. (Note + that cloning with a plain path just hardlinks everything!) + +If you really don't want to clone it, for whatever reasons, check the +following points instead (in this order). This is a very destructive +approach, so *make a backup* or go back to cloning it. You have been +warned. + +* Remove the original refs backed up by git-filter-branch: say `git + for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git + update-ref -d`. + +* Expire all reflogs with `git reflog expire \--expire=now \--all`. + +* Garbage collect all unreferenced objects with `git gc \--prune=now` + (or if your git-gc is not new enough to support arguments to + `\--prune`, use `git repack -ad; git prune` instead). + + Author ------ Written by Petr "Pasky" Baudis <pasky@suse.cz>, diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 7086eea74a..b292e9843a 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- -'git gc' [--aggressive] [--auto] [--quiet] +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] DESCRIPTION ----------- @@ -59,6 +59,14 @@ are consolidated into a single pack by using the `-A` option of 'git-repack'. Setting `gc.autopacklimit` to 0 disables automatic consolidation of packs. +--prune=<date>:: + Prune loose objects older than date (default is 2 weeks ago, + overrideable by the config variable `gc.pruneExpire`). This + option is on by default. + +--no-prune:: + Do not prune any loose objects. + --quiet:: Suppress all progress reports. diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index bd49a0aee8..1685f04efe 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -98,6 +98,20 @@ Using direct mode with SSL: .......................... +CAUTION +------- +It is still your responsibility to make sure that the email message +sent by your email program meets the standards of your project. +Many projects do not like patches to be attached. Some mail +agents will transform patches (e.g. wrap lines, send them as +format=flowed) in ways that make them fail. You will get angry +flames ridiculing you if you don't check this. + +Thunderbird in particular is known to be problematic. Thunderbird +users may wish to visit this web page for more information: + http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email + + BUGS ---- Doesn't handle lines starting with "From " in the message body. diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt deleted file mode 100644 index 3d93625f9a..0000000000 --- a/Documentation/git-notes.txt +++ /dev/null @@ -1,46 +0,0 @@ -git-notes(1) -============ - -NAME ----- -git-notes - Add/inspect commit notes - -SYNOPSIS --------- -[verse] -'git-notes' (edit | show) [commit] - -DESCRIPTION ------------ -This command allows you to add notes to commit messages, without -changing the commit. To discern these notes from the message stored -in the commit object, the notes are indented like the message, after -an unindented line saying "Notes:". - -To disable commit notes, you have to set the config variable -core.notesRef to the empty string. Alternatively, you can set it -to a different ref, something like "refs/notes/bugzilla". This setting -can be overridden by the environment variable "GIT_NOTES_REF". - - -SUBCOMMANDS ------------ - -edit:: - Edit the notes for a given commit (defaults to HEAD). - -show:: - Show the notes for a given commit (defaults to HEAD). - - -Author ------- -Written by Johannes Schindelin <johannes.schindelin@gmx.de> - -Documentation -------------- -Documentation by Johannes Schindelin - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 3d6d429e5e..30487de48f 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -246,6 +246,7 @@ OPTIONS --whitespace=<nowarn|warn|error|error-all|strip>:: This flag is passed to the 'git-apply' program (see linkgit:git-apply[1]) that applies the patch. + Incompatible with the --interactive option. -i:: --interactive:: diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 7b654f7928..3d456545d7 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -499,6 +499,14 @@ svn-remote.<name>.rewriteRoot:: the repository with a public http:// or svn:// URL in the metadata so users of it will see the public URL. +svn.brokenSymlinkWorkaround:: +This disables potentially expensive checks to workaround broken symlinks +checked into SVN by broken clients. Set this option to "false" if you +track a SVN repository with many empty blobs that are not symlinks. +This option may be changed while "git-svn" is running and take effect on +the next revision fetched. If unset, git-svn assumes this option to be +"true". + -- Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index 4673a75a98..bd005bc5c8 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -47,7 +47,8 @@ frequently used options. After an attempt to merge stops with conflicts, show the commits on the history between two branches (i.e. the HEAD and the MERGE_HEAD) - that modify the conflicted files. + that modify the conflicted files and do not exist on all the heads + being merged. --argscmd=<command>:: Command to be run each time gitk has to determine the list of @@ -228,7 +228,7 @@ GITWEB_FAVICON = git-favicon.png GITWEB_SITE_HEADER = GITWEB_SITE_FOOTER = -export prefix bindir sharedir htmldir sysconfdir +export prefix bindir sharedir sysconfdir CC = gcc AR = ar @@ -265,7 +265,6 @@ SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh -SCRIPT_SH += git-notes.sh SCRIPT_SH += git-parse-remote.sh SCRIPT_SH += git-pull.sh SCRIPT_SH += git-quiltimport.sh @@ -318,8 +317,8 @@ PROGRAMS += git-var$X # builtin-$C.o but is linked in as part of some other command. BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) -BUILT_INS += git-cherry-pick$X BUILT_INS += git-cherry$X +BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X BUILT_INS += git-fsck-objects$X BUILT_INS += git-get-tar-commit-id$X @@ -358,8 +357,8 @@ LIB_H += builtin.h LIB_H += cache.h LIB_H += cache-tree.h LIB_H += commit.h -LIB_H += compat/mingw.h LIB_H += compat/cygwin.h +LIB_H += compat/mingw.h LIB_H += csum-file.h LIB_H += decorate.h LIB_H += delta.h @@ -378,14 +377,12 @@ LIB_H += ll-merge.h LIB_H += log-tree.h LIB_H += mailmap.h LIB_H += merge-recursive.h -LIB_H += notes.h LIB_H += object.h LIB_H += pack.h LIB_H += pack-refs.h LIB_H += pack-revindex.h LIB_H += parse-options.h LIB_H += patch-ids.h -LIB_H += string-list.h LIB_H += pkt-line.h LIB_H += progress.h LIB_H += quote.h @@ -399,6 +396,7 @@ LIB_H += sha1-lookup.h LIB_H += sideband.h LIB_H += sigchain.h LIB_H += strbuf.h +LIB_H += string-list.h LIB_H += tag.h LIB_H += transport.h LIB_H += tree.h @@ -437,8 +435,8 @@ LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o LIB_OBJS += diff-delta.o -LIB_OBJS += diff-no-index.o LIB_OBJS += diff-lib.o +LIB_OBJS += diff-no-index.o LIB_OBJS += diff.o LIB_OBJS += dir.o LIB_OBJS += editor.o @@ -461,7 +459,6 @@ LIB_OBJS += match-trees.o LIB_OBJS += merge-file.o LIB_OBJS += merge-recursive.o LIB_OBJS += name-hash.o -LIB_OBJS += notes.o LIB_OBJS += object.o LIB_OBJS += pack-check.o LIB_OBJS += pack-refs.o @@ -471,9 +468,9 @@ LIB_OBJS += pager.o LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o LIB_OBJS += patch-ids.o -LIB_OBJS += string-list.o LIB_OBJS += path.o LIB_OBJS += pkt-line.o +LIB_OBJS += preload-index.o LIB_OBJS += pretty.o LIB_OBJS += progress.o LIB_OBJS += quote.o @@ -487,13 +484,14 @@ LIB_OBJS += revision.o LIB_OBJS += run-command.o LIB_OBJS += server-info.o LIB_OBJS += setup.o -LIB_OBJS += sha1_file.o LIB_OBJS += sha1-lookup.o +LIB_OBJS += sha1_file.o LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o LIB_OBJS += sigchain.o LIB_OBJS += strbuf.o +LIB_OBJS += string-list.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += trace.o @@ -502,8 +500,8 @@ LIB_OBJS += tree-diff.o LIB_OBJS += tree.o LIB_OBJS += tree-walk.o LIB_OBJS += unpack-trees.o -LIB_OBJS += userdiff.o LIB_OBJS += usage.o +LIB_OBJS += userdiff.o LIB_OBJS += utf8.o LIB_OBJS += walker.o LIB_OBJS += wrapper.o @@ -511,7 +509,6 @@ LIB_OBJS += write_or_die.o LIB_OBJS += ws.o LIB_OBJS += wt-status.o LIB_OBJS += xdiff-interface.o -LIB_OBJS += preload-index.o BUILTIN_OBJS += builtin-add.o BUILTIN_OBJS += builtin-annotate.o @@ -132,7 +132,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base, err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0); if (err) return err; - return READ_TREE_RECURSIVE; + return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } buffer = sha1_file_to_archive(path_without_prefix, sha1, mode, @@ -103,14 +103,22 @@ void create_branch(const char *head, struct ref_lock *lock; struct commit *commit; unsigned char sha1[20]; - char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20]; + char *real_ref, msg[PATH_MAX + 20]; + struct strbuf ref = STRBUF_INIT; int forcing = 0; + int len; - snprintf(ref, sizeof ref, "refs/heads/%s", name); - if (check_ref_format(ref)) + len = strlen(name); + if (interpret_nth_last_branch(name, &ref) != len) { + strbuf_reset(&ref); + strbuf_add(&ref, name, len); + } + strbuf_splice(&ref, 0, 0, "refs/heads/", 11); + + if (check_ref_format(ref.buf)) die("'%s' is not a valid branch name.", name); - if (resolve_ref(ref, sha1, 1, NULL)) { + if (resolve_ref(ref.buf, sha1, 1, NULL)) { if (!force) die("A branch named '%s' already exists.", name); else if (!is_bare_repository() && !strcmp(head, name)) @@ -142,7 +150,7 @@ void create_branch(const char *head, die("Not a valid branch point: '%s'.", start_name); hashcpy(sha1, commit->object.sha1); - lock = lock_any_ref_for_update(ref, NULL, 0); + lock = lock_any_ref_for_update(ref.buf, NULL, 0); if (!lock) die("Failed to lock ref for update: %s.", strerror(errno)); @@ -162,6 +170,7 @@ void create_branch(const char *head, if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); + strbuf_release(&ref); free(real_ref); } diff --git a/builtin-branch.c b/builtin-branch.c index 56a1971d69..504a981ad5 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -99,6 +99,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) const char *fmt, *remote; int i; int ret = 0; + struct strbuf bname = STRBUF_INIT; switch (kinds) { case REF_REMOTE_BRANCH: @@ -119,20 +120,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!head_rev) die("Couldn't look up commit object for HEAD"); } - for (i = 0; i < argc; i++) { - if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) { + for (i = 0; i < argc; i++, strbuf_release(&bname)) { + int len = strlen(argv[i]); + + if (interpret_nth_last_branch(argv[i], &bname) != len) + strbuf_add(&bname, argv[i], len); + + if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { error("Cannot delete the branch '%s' " - "which you are currently on.", argv[i]); + "which you are currently on.", bname.buf); ret = 1; continue; } free(name); - name = xstrdup(mkpath(fmt, argv[i])); + name = xstrdup(mkpath(fmt, bname.buf)); if (!resolve_ref(name, sha1, 1, NULL)) { error("%sbranch '%s' not found.", - remote, argv[i]); + remote, bname.buf); ret = 1; continue; } @@ -152,22 +158,23 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { error("The branch '%s' is not an ancestor of " - "your current HEAD.\n" - "If you are sure you want to delete it, " - "run 'git branch -D %s'.", argv[i], argv[i]); + "your current HEAD.\n" + "If you are sure you want to delete it, " + "run 'git branch -D %s'.", bname.buf, bname.buf); ret = 1; continue; } if (delete_ref(name, sha1, 0)) { error("Error deleting %sbranch '%s'", remote, - argv[i]); + bname.buf); ret = 1; } else { struct strbuf buf = STRBUF_INIT; - printf("Deleted %sbranch %s (%s).\n", remote, argv[i], - find_unique_abbrev(sha1, DEFAULT_ABBREV)); - strbuf_addf(&buf, "branch.%s", argv[i]); + printf("Deleted %sbranch %s (%s).\n", remote, + bname.buf, + find_unique_abbrev(sha1, DEFAULT_ABBREV)); + strbuf_addf(&buf, "branch.%s", bname.buf); if (git_config_rename_section(buf.buf, NULL) < 0) warning("Update of config-file failed"); strbuf_release(&buf); diff --git a/builtin-clone.c b/builtin-clone.c index f73029e2ba..c338910b1c 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -350,6 +350,19 @@ static struct ref *write_remote_refs(const struct ref *refs, return local_refs; } +static void install_branch_config(const char *local, + const char *origin, + const char *remote) +{ + struct strbuf key = STRBUF_INIT; + strbuf_addf(&key, "branch.%s.remote", local); + git_config_set(key.buf, origin); + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", local); + git_config_set(key.buf, remote); + strbuf_release(&key); +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int use_local_hardlinks = 1; @@ -539,6 +552,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; + if (!option_bare) + install_branch_config("master", option_origin, + "refs/heads/master"); } if (head_points_at) { @@ -567,11 +583,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - strbuf_addf(&key, "branch.%s.remote", head); - git_config_set(key.buf, option_origin); - strbuf_reset(&key); - strbuf_addf(&key, "branch.%s.merge", head); - git_config_set(key.buf, head_points_at->name); + install_branch_config(head, option_origin, + head_points_at->name); } } else if (remote_head) { /* Source had detached HEAD pointing somewhere. */ diff --git a/builtin-commit.c b/builtin-commit.c index d6a3a6203a..46e649cd7c 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -561,7 +561,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix) commitable = run_status(fp, index_file, prefix, 1); wt_status_use_color = saved_color_setting; } else { - struct rev_info rev; unsigned char sha1[20]; const char *parent = "HEAD"; @@ -573,16 +572,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix) if (get_sha1(parent, sha1)) commitable = !!active_nr; - else { - init_revisions(&rev, ""); - rev.abbrev = 0; - setup_revisions(0, NULL, &rev, parent); - DIFF_OPT_SET(&rev.diffopt, QUIET); - DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); - run_diff_index(&rev, 1 /* cached */); - - commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); - } + else + commitable = index_differs_from(parent, 0); } fclose(fp); diff --git a/builtin-fast-export.c b/builtin-fast-export.c index e9ee2c79ac..fdf4ae9ebd 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -514,6 +514,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) get_tags_and_duplicates(&revs.pending, &extra_refs); + revs.topo_order = 1; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); revs.diffopt.format_callback = show_filemodify; diff --git a/builtin-gc.c b/builtin-gc.c index a2014388da..8d990ed493 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -161,7 +161,8 @@ static int need_to_gc(void) */ if (too_many_packs()) append_option(argv_repack, - !strcmp(prune_expire, "now") ? "-a" : "-A", + prune_expire && !strcmp(prune_expire, "now") ? + "-a" : "-A", MAX_ADD); else if (!too_many_loose_objects()) return 0; @@ -173,14 +174,15 @@ static int need_to_gc(void) int cmd_gc(int argc, const char **argv, const char *prefix) { - int prune = 0; int aggressive = 0; int auto_gc = 0; int quiet = 0; char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"), + { OPTION_STRING, 0, "prune", &prune_expire, "date", + "prune unreferenced objects", + PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"), @@ -218,7 +220,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) "\"git help gc\" for more information.\n"); } else append_option(argv_repack, - !strcmp(prune_expire, "now") ? "-a" : "-A", + prune_expire && !strcmp(prune_expire, "now") + ? "-a" : "-A", MAX_ADD); if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) @@ -230,9 +233,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (run_command_v_opt(argv_repack, RUN_GIT_CMD)) return error(FAILED_RUN, argv_repack[0]); - argv_prune[2] = prune_expire; - if (run_command_v_opt(argv_prune, RUN_GIT_CMD)) - return error(FAILED_RUN, argv_prune[0]); + if (prune_expire) { + argv_prune[2] = prune_expire; + if (run_command_v_opt(argv_prune, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_prune[0]); + } if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) return error(FAILED_RUN, argv_rerere[0]); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 3434031295..9dec282fba 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -262,6 +262,21 @@ static const char *verify_pathspec(const char *prefix) return max ? xmemdupz(prev, max) : NULL; } +static void strip_trailing_slash_from_submodules(void) +{ + const char **p; + + for (p = pathspec; *p != NULL; p++) { + int len = strlen(*p), pos; + + if (len < 1 || (*p)[len - 1] != '/') + continue; + pos = cache_name_pos(*p, len - 1); + if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode)) + *p = xstrndup(*p, len - 1); + } +} + /* * Read the tree specified with --with-tree option * (typically, HEAD) into stage #1 and then @@ -510,6 +525,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) pathspec = get_pathspec(prefix, argv + i); + /* be nice with submodule patsh ending in a slash */ + read_cache(); + if (pathspec) + strip_trailing_slash_from_submodules(); + /* Verify that the pathspec matches the prefix */ if (pathspec) prefix = verify_pathspec(prefix); @@ -533,7 +553,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) show_killed | show_modified)) show_cached = 1; - read_cache(); if (prefix) prune_cache(prefix); if (with_tree) { diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index 5b63e6eada..fca46312f6 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -68,13 +68,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, * * Something similar to this incomplete example: * - if (show_subprojects(base, baselen, pathname)) { - struct child_process ls_tree; - - ls_tree.dir = base; - ls_tree.argv = ls-tree; - start_command(&ls_tree); - } + if (show_subprojects(base, baselen, pathname)) + retval = READ_TREE_RECURSIVE; * */ type = commit_type; diff --git a/builtin-merge.c b/builtin-merge.c index 885fad9bba..6d2160d0a3 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -356,9 +356,14 @@ static void merge_name(const char *remote, struct strbuf *msg) struct object *remote_head; unsigned char branch_head[20], buf_sha[20]; struct strbuf buf = STRBUF_INIT; + struct strbuf bname = STRBUF_INIT; const char *ptr; int len, early; + len = strlen(remote); + if (interpret_nth_last_branch(remote, &bname) == len) + remote = bname.buf; + memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); if (!remote_head) @@ -371,7 +376,7 @@ static void merge_name(const char *remote, struct strbuf *msg) if (!hashcmp(remote_head->sha1, branch_head)) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); - return; + goto cleanup; } /* See if remote matches <name>^^^.. or <name>~<number> */ @@ -411,7 +416,8 @@ static void merge_name(const char *remote, struct strbuf *msg) sha1_to_hex(remote_head->sha1), truname.buf + 11, (early ? " (early part)" : "")); - return; + strbuf_release(&truname); + goto cleanup; } } @@ -432,10 +438,13 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); - return; + goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->sha1), remote); +cleanup: + strbuf_release(&buf); + strbuf_release(&bname); } static int git_merge_config(const char *k, const char *v, void *cb) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 6de186c397..f7e04c45f9 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -18,14 +18,16 @@ enum deny_action { DENY_REFUSE, }; -static int deny_deletes = 0; -static int deny_non_fast_forwards = 0; +static int deny_deletes; +static int deny_non_fast_forwards; static enum deny_action deny_current_branch = DENY_UNCONFIGURED; +static enum deny_action deny_delete_current = DENY_UNCONFIGURED; static int receive_fsck_objects; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int unpack_limit = 100; static int report_status; +static const char *head_name; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; @@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.denydeletecurrent") == 0) { + deny_delete_current = parse_deny_action(var, value); + return 0; + } + return git_default_config(var, value, cb); } @@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd) static int is_ref_checked_out(const char *ref) { - unsigned char sha1[20]; - const char *head; - if (is_bare_repository()) return 0; - head = resolve_ref("HEAD", sha1, 0, NULL); - if (!head) + if (!head_name) return 0; - return !strcmp(head, ref); + return !strcmp(head_name, ref); } static char *warn_unconfigured_deny_msg[] = { @@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void) warning(warn_unconfigured_deny_msg[i]); } +static char *warn_unconfigured_deny_delete_current_msg[] = { + "Deleting the current branch can cause confusion by making the next", + "'git clone' not check out any file.", + "", + "You can set 'receive.denyDeleteCurrent' configuration variable to", + "'refuse' in the remote repository to disallow deleting the current", + "branch.", + "", + "You can set it to 'ignore' to allow such a delete without a warning.", + "", + "To make this warning message less loud, you can set it to 'warn'.", + "", + "Note that the default will change in a future version of git", + "to refuse deleting the current branch unless you have the", + "configuration variable set to either 'ignore' or 'warn'." +}; + +static void warn_unconfigured_deny_delete_current(void) +{ + int i; + for (i = 0; + i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg); + i++) + warning(warn_unconfigured_deny_delete_current_msg[i]); +} + static const char *update(struct command *cmd) { const char *name = cmd->ref_name; @@ -278,12 +307,30 @@ static const char *update(struct command *cmd) "but I can't find it!", sha1_to_hex(new_sha1)); return "bad pack"; } - if (deny_deletes && is_null_sha1(new_sha1) && - !is_null_sha1(old_sha1) && - !prefixcmp(name, "refs/heads/")) { - error("denying ref deletion for %s", name); - return "deletion prohibited"; + + if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { + if (deny_deletes && !prefixcmp(name, "refs/heads/")) { + error("denying ref deletion for %s", name); + return "deletion prohibited"; + } + + if (!strcmp(name, head_name)) { + switch (deny_delete_current) { + case DENY_IGNORE: + break; + case DENY_WARN: + case DENY_UNCONFIGURED: + if (deny_delete_current == DENY_UNCONFIGURED) + warn_unconfigured_deny_delete_current(); + warning("deleting the current branch"); + break; + case DENY_REFUSE: + error("refusing to delete the current branch: %s", name); + return "deletion of the current branch prohibited"; + } + } } + if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && !prefixcmp(name, "refs/heads/")) { @@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd) static void execute_commands(const char *unpacker_error) { struct command *cmd = commands; + unsigned char sha1[20]; if (unpacker_error) { while (cmd) { @@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error) return; } + head_name = resolve_ref("HEAD", sha1, 0, NULL); + while (cmd) { cmd->error_string = update(cmd); cmd = cmd->next; diff --git a/builtin-remote.c b/builtin-remote.c index db18bcfc97..ac69d37c8a 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -756,12 +756,17 @@ static int prune(int argc, const char **argv) OPT_END() }; struct ref_states states; + const char *dangling_msg; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); if (argc < 1) usage_with_options(builtin_remote_usage, options); + dangling_msg = (dry_run + ? " %s will become dangling!\n" + : " %s has become dangling!\n"); + memset(&states, 0, sizeof(states)); for (; argc; argc--, argv++) { int i; @@ -784,6 +789,7 @@ static int prune(int argc, const char **argv) printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", abbrev_ref(refname, "refs/remotes/")); + warn_dangling_symref(dangling_msg, refname); } /* NEEDSWORK: free remote */ diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 857742a14f..436afa45f5 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -608,6 +608,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--bisect-all")) { bisect_list = 1; bisect_find_all = 1; + revs.show_decorations = 1; continue; } if (!strcmp(arg, "--bisect-vars")) { diff --git a/builtin-revert.c b/builtin-revert.c index d48313c745..d210150671 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -223,17 +223,6 @@ static char *help_msg(const unsigned char *sha1) return helpbuf; } -static int index_is_dirty(void) -{ - struct rev_info rev; - init_revisions(&rev, NULL); - setup_revisions(0, NULL, &rev, "HEAD"); - DIFF_OPT_SET(&rev.diffopt, QUIET); - DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); - run_diff_index(&rev, 1); - return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); -} - static struct tree *empty_tree(void) { struct tree *tree = xcalloc(1, sizeof(struct tree)); @@ -279,7 +268,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) } else { if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); - if (index_is_dirty()) + if (index_differs_from("HEAD", 0)) die ("Dirty index: cannot %s", me); } discard_cache(); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index cafc4eba7c..6ae6bcc0e8 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -45,8 +45,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) break; case 2: if (!strcmp(argv[0], "HEAD") && - prefixcmp(argv[1], "refs/heads/")) - die("Refusing to point HEAD outside of refs/heads/"); + prefixcmp(argv[1], "refs/")) + die("Refusing to point HEAD outside of refs/"); create_symref(argv[0], argv[1], msg); break; default: @@ -371,8 +371,6 @@ static inline enum object_type object_type(unsigned int mode) #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" -#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF" -#define GIT_NOTES_DEFAULT_REF "refs/notes/commits" extern int is_bare_repository_cfg; extern int is_bare_repository(void); @@ -544,7 +542,6 @@ enum rebase_setup_type { extern enum branch_track git_branch_track; extern enum rebase_setup_type autorebase; -extern char *notes_ref_name; #define GIT_REPO_VERSION 0 extern int repository_format_version; @@ -627,7 +624,7 @@ int is_directory(const char *); const char *make_absolute_path(const char *path); const char *make_nonrelative_path(const char *path); const char *make_relative_path(const char *abs, const char *base); -int normalize_absolute_path(char *buf, const char *path); +int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, const char *prefix_list); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ @@ -830,6 +827,7 @@ extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t extern void close_pack_windows(struct packed_git *); extern void unuse_pack(struct pack_window **); extern void free_pack_by_name(const char *); +extern void clear_delta_base_cache(void); extern struct packed_git *add_packed_git(const char *, int, int); extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t); extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t); diff --git a/command-list.txt b/command-list.txt index 2dc2c3320c..3583a33ee9 100644 --- a/command-list.txt +++ b/command-list.txt @@ -73,7 +73,6 @@ git-mktag plumbingmanipulators git-mktree plumbingmanipulators git-mv mainporcelain common git-name-rev plumbinginterrogators -git-notes mainporcelain git-pack-objects plumbingmanipulators git-pack-redundant plumbinginterrogators git-pack-refs ancillarymanipulators @@ -5,7 +5,6 @@ #include "utf8.h" #include "diff.h" #include "revision.h" -#include "notes.h" int save_commit_buffer = 1; @@ -469,11 +469,6 @@ static int git_default_core_config(const char *var, const char *value) return 0; } - if (!strcmp(var, "core.notesref")) { - notes_ref_name = xstrdup(value); - return 0; - } - if (!strcmp(var, "core.pager")) return git_config_string(&pager_program, var, value); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 307bf5d4f9..412d2c0dab 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,11 +34,11 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # -# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty -# value, unstaged (*) and staged (+) changes will be shown next -# to the branch name. You can configure this per-repository -# with the bash.showDirtyState variable, which defaults to true -# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. # # To submit patches: # @@ -125,7 +125,7 @@ __git_ps1 () local w local i - if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then if test "$(git config --bool bash.showDirtyState)" != "false"; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" @@ -1196,10 +1196,14 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.status) + color.branch|color.diff|color.interactive|color.status|color.ui) __gitcomp "always never auto" return ;; + color.pager) + __gitcomp "false true" + return + ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white @@ -1606,7 +1610,7 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 28a3c0e46e..60cbab65d3 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -615,7 +615,9 @@ show_new_revisions() revspec=$oldrev..$newrev fi - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ | + grep -F -v $refname) + git rev-parse --not $other_branches | if [ -z "$custom_showrev" ] then git rev-list --pretty --stdin $revspec diff --git a/diff-lib.c b/diff-lib.c index a41e1ec07c..79d0606834 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -513,3 +513,18 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) exit(128); return 0; } + +int index_differs_from(const char *def, int diff_flags) +{ + struct rev_info rev; + + init_revisions(&rev, NULL); + setup_revisions(0, NULL, &rev, def); + DIFF_OPT_SET(&rev.diffopt, QUIET); + DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); + rev.diffopt.flags |= diff_flags; + run_diff_index(&rev, 1); + if (rev.pending.alloc) + free(rev.pending.objects); + return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0); +} @@ -184,11 +184,11 @@ static int remove_tempfile_installed; static void remove_tempfile(void) { int i; - for (i = 0; i < ARRAY_SIZE(diff_temp); i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { + if (diff_temp[i].name == diff_temp[i].tmp_path) unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } + diff_temp[i].name = NULL; + } } static void remove_tempfile_on_signal(int signo) @@ -2326,15 +2326,12 @@ void diff_setup(struct diff_options *options) options->break_opt = -1; options->rename_limit = -1; options->dirstat_percent = 3; - DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE); options->context = 3; options->change = diff_change; options->add_remove = diff_addremove; if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); - else - DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; if (!diff_mnemonic_prefix) { @@ -265,4 +265,6 @@ extern int diff_result_code(struct diff_options *, int); extern void diff_no_index(struct rev_info *, int, const char **, int, const char *); +extern int index_differs_from(const char *def, int diff_flags); + #endif /* DIFF_H */ diff --git a/environment.c b/environment.c index 0edae21e74..e278bce0ea 100644 --- a/environment.c +++ b/environment.c @@ -45,7 +45,6 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER; /* Parallel index stat data preload? */ int core_preload_index = 0; -char *notes_ref_name; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --git a/fast-import.c b/fast-import.c index 1935206be0..3ef3413e69 100644 --- a/fast-import.c +++ b/fast-import.c @@ -869,7 +869,7 @@ static char *create_index(void) /* Generate the fan-out array. */ c = idx; for (i = 0; i < 256; i++) { - struct object_entry **next = c;; + struct object_entry **next = c; while (next < last) { if ((*next)->sha1[0] != i) break; @@ -945,6 +945,7 @@ static void end_packfile(void) { struct packed_git *old_p = pack_data, *new_p; + clear_delta_base_cache(); if (object_count) { char *idx_name; int i; diff --git a/git-add--interactive.perl b/git-add--interactive.perl index ec47888f57..5f129a4203 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -14,7 +14,8 @@ my ($prompt_color, $header_color, $help_color) = ) : (); my $error_color = (); if ($menu_use_color) { - my $help_color_spec = $repo->config('color.interactive.help'); + my $help_color_spec = ($repo->config('color.interactive.help') or + 'red bold'); $error_color = $repo->get_color('color.interactive.error', $help_color_spec); } diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 86eef56297..27b57b826a 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -221,7 +221,7 @@ die "" trap 'cd ../..; rm -rf "$tempdir"' 0 # Make sure refs/original is empty -git for-each-ref > "$tempdir"/backup-refs +git for-each-ref > "$tempdir"/backup-refs || exit while read sha1 type name do case "$force,$name" in @@ -241,8 +241,9 @@ GIT_WORK_TREE=. export GIT_DIR GIT_WORK_TREE # The refs should be updated if their heads were rewritten -git rev-parse --no-flags --revs-only --symbolic-full-name --default HEAD "$@" | -sed -e '/^^/d' >"$tempdir"/heads +git rev-parse --no-flags --revs-only --symbolic-full-name \ + --default HEAD "$@" > "$tempdir"/raw-heads || exit +sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads test -s "$tempdir"/heads || die "Which ref do you want to rewrite?" @@ -251,8 +252,6 @@ GIT_INDEX_FILE="$(pwd)/../index" export GIT_INDEX_FILE git read-tree || die "Could not seed the index" -ret=0 - # map old->new commit ids for rewriting parents mkdir ../map || die "Could not create map/ directory" @@ -315,10 +314,11 @@ while read commit parents; do die "tree filter failed: $filter_tree" ( - git diff-index -r --name-only $commit + git diff-index -r --name-only $commit && git ls-files --others - ) | - git update-index --add --replace --remove --stdin + ) > "$tempdir"/tree-state || exit + git update-index --add --replace --remove --stdin \ + < "$tempdir"/tree-state || exit fi eval "$filter_index" < /dev/null || @@ -339,7 +339,8 @@ while read commit parents; do eval "$filter_msg" > ../message || die "msg filter failed: $filter_msg" @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \ - $(git write-tree) $parentstr < ../message > ../map/$commit + $(git write-tree) $parentstr < ../message > ../map/$commit || + die "could not write rewritten commit" done <../revs # In case of a subdirectory filter, it is possible that a specified head @@ -407,7 +408,8 @@ do die "Could not rewrite $ref" ;; esac - git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 + git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 || + exit done < "$tempdir"/heads # TODO: This should possibly go, with the semantics that all positive given @@ -483,7 +485,7 @@ test -z "$ORIG_GIT_INDEX_FILE" || { } if [ "$(is_bare_repository)" = false ]; then - git read-tree -u -m HEAD + git read-tree -u -m HEAD || exit fi -exit $ret +exit 0 diff --git a/git-notes.sh b/git-notes.sh deleted file mode 100755 index bfdbaa8527..0000000000 --- a/git-notes.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh - -USAGE="(edit | show) [commit]" -. git-sh-setup - -test -n "$3" && usage - -test -z "$1" && usage -ACTION="$1"; shift - -test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)" -test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits" - -COMMIT=$(git rev-parse --verify --default HEAD "$@") || -die "Invalid commit: $@" - -MESSAGE="$GIT_DIR"/new-notes-$COMMIT -trap ' - test -f "$MESSAGE" && rm "$MESSAGE" -' 0 - -case "$ACTION" in -edit) - GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MESSAGE" - - GIT_INDEX_FILE="$MESSAGE".idx - export GIT_INDEX_FILE - - CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ') - if [ -z "$CURRENT_HEAD" ]; then - PARENT= - else - PARENT="-p $CURRENT_HEAD" - git read-tree "$GIT_NOTES_REF" || die "Could not read index" - git cat-file blob :$COMMIT >> "$MESSAGE" 2> /dev/null - fi - - ${VISUAL:-${EDITOR:-vi}} "$MESSAGE" - - grep -v ^# < "$MESSAGE" | git stripspace > "$MESSAGE".processed - mv "$MESSAGE".processed "$MESSAGE" - if [ -s "$MESSAGE" ]; then - BLOB=$(git hash-object -w "$MESSAGE") || - die "Could not write into object database" - git update-index --add --cacheinfo 0644 $BLOB $COMMIT || - die "Could not write index" - else - test -z "$CURRENT_HEAD" && - die "Will not initialise with empty tree" - git update-index --force-remove $COMMIT || - die "Could not update index" - fi - - TREE=$(git write-tree) || die "Could not write tree" - NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) || - die "Could not annotate" - git update-ref -m "Annotate $COMMIT" \ - "$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD -;; -show) - git show "$GIT_NOTES_REF":$COMMIT -;; -*) - usage -esac diff --git a/git-repack.sh b/git-repack.sh index 458a497af8..be6db5e805 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -88,32 +88,79 @@ if [ -z "$names" ]; then echo Nothing new to pack. fi fi -for name in $names ; do - fullbases="$fullbases pack-$name" - chmod a-w "$PACKTMP-$name.pack" - chmod a-w "$PACKTMP-$name.idx" - mkdir -p "$PACKDIR" || exit +# Ok we have prepared all new packfiles. +mkdir -p "$PACKDIR" || exit + +# First see if there are packs of the same name and if so +# if we can move them out of the way (this can happen if we +# repacked immediately after packing fully. +rollback= +failed= +for name in $names +do for sfx in pack idx do - if test -f "$PACKDIR/pack-$name.$sfx" - then - mv -f "$PACKDIR/pack-$name.$sfx" \ - "$PACKDIR/old-pack-$name.$sfx" - fi - done && + file=pack-$name.$sfx + test -f "$PACKDIR/$file" || continue + rm -f "$PACKDIR/old-$file" && + mv "$PACKDIR/$file" "$PACKDIR/old-$file" || { + failed=t + break + } + rollback="$rollback $file" + done + test -z "$failed" || break +done + +# If renaming failed for any of them, roll the ones we have +# already renamed back to their original names. +if test -n "$failed" +then + rollback_failure= + for file in $rollback + do + mv "$PACKDIR/old-$file" "$PACKDIR/$file" || + rollback_failure="$rollback_failure $file" + done + if test -n "$rollback_failure" + then + echo >&2 "WARNING: Some packs in use have been renamed by" + echo >&2 "WARNING: prefixing old- to their name, in order to" + echo >&2 "WARNING: replace them with the new version of the" + echo >&2 "WARNING: file. But the operation failed, and" + echo >&2 "WARNING: attempt to rename them back to their" + echo >&2 "WARNING: original names also failed." + echo >&2 "WARNING: Please rename them in $PACKDIR manually:" + for file in $rollback_failure + do + echo >&2 "WARNING: old-$file -> $file" + done + fi + exit 1 +fi + +# Now the ones with the same name are out of the way... +fullbases= +for name in $names +do + fullbases="$fullbases pack-$name" + chmod a-w "$PACKTMP-$name.pack" + chmod a-w "$PACKTMP-$name.idx" mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" && - mv -f "$PACKTMP-$name.idx" "$PACKDIR/pack-$name.idx" && - test -f "$PACKDIR/pack-$name.pack" && - test -f "$PACKDIR/pack-$name.idx" || { - echo >&2 "Couldn't replace the existing pack with updated one." - echo >&2 "The original set of packs have been saved as" - echo >&2 "old-pack-$name.{pack,idx} in $PACKDIR." - exit 1 - } - rm -f "$PACKDIR/old-pack-$name.pack" "$PACKDIR/old-pack-$name.idx" + mv -f "$PACKTMP-$name.idx" "$PACKDIR/pack-$name.idx" || + exit +done + +# Remove the "old-" files +for name in $names +do + rm -f "$PACKDIR/old-pack-$name.idx" + rm -f "$PACKDIR/old-pack-$name.pack" done +# End of pack replacement. + if test "$remove_redundant" = t then # We know $existing are all redundant. diff --git a/git-submodule.sh b/git-submodule.sh index 2f47e065fe..6cc2d334c0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -59,7 +59,7 @@ resolve_relative_url () # module_list() { - git ls-files --stage -- "$@" | grep '^160000 ' + git ls-files --error-unmatch --stage -- "$@" | grep '^160000 ' } # diff --git a/git-svn.perl b/git-svn.perl index 79888a05c4..001a1d8eff 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2389,22 +2389,8 @@ sub find_parent_branch { print STDERR "Found possible branch point: ", "$new_url => ", $self->full_url, ", $r\n"; $branch_from =~ s#^/##; - my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from); - unless ($gs) { - my $ref_id = $self->{ref_id}; - $ref_id =~ s/\@\d+$//; - $ref_id .= "\@$r"; - # just grow a tail if we're not unique enough :x - $ref_id .= '-' while find_ref($ref_id); - print STDERR "Initializing parent: $ref_id\n"; - my ($u, $p, $repo_id) = ($new_url, '', $ref_id); - if ($u =~ s#^\Q$url\E(/|$)##) { - $p = $u; - $u = $url; - $repo_id = $self->{repo_id}; - } - $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1); - } + my $gs = $self->other_gs($new_url, $url, $repos_root, + $branch_from, $r, $self->{ref_id}); my ($r0, $parent) = $gs->find_rev_before($r, 1); { my ($base, $head); @@ -2431,7 +2417,7 @@ sub find_parent_branch { # is not included with SVN 1.4.3 (the latest version # at the moment), so we can't rely on it $self->{last_commit} = $parent; - $ed = SVN::Git::Fetcher->new($self); + $ed = SVN::Git::Fetcher->new($self, $gs->{path}); $gs->ra->gs_do_switch($r0, $rev, $gs, $self->full_url, $ed) or die "SVN connection failed somewhere...\n"; @@ -2586,6 +2572,28 @@ sub parse_svn_date { return $parsed_date; } +sub other_gs { + my ($self, $new_url, $url, $repos_root, + $branch_from, $r, $old_ref_id) = @_; + my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from); + unless ($gs) { + my $ref_id = $old_ref_id; + $ref_id =~ s/\@\d+$//; + $ref_id .= "\@$r"; + # just grow a tail if we're not unique enough :x + $ref_id .= '-' while find_ref($ref_id); + print STDERR "Initializing parent: $ref_id\n"; + my ($u, $p, $repo_id) = ($new_url, '', $ref_id); + if ($u =~ s#^\Q$url\E(/|$)##) { + $p = $u; + $u = $url; + $repo_id = $self->{repo_id}; + } + $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1); + } + $gs +} + sub check_author { my ($author) = @_; if (!defined $author || length $author == 0) { @@ -3250,12 +3258,13 @@ use vars qw/$_ignore_regex/; # file baton members: path, mode_a, mode_b, pool, fh, blob, base sub new { - my ($class, $git_svn) = @_; + my ($class, $git_svn, $switch_path) = @_; my $self = SVN::Delta::Editor->new; bless $self, $class; if (exists $git_svn->{last_commit}) { $self->{c} = $git_svn->{last_commit}; - $self->{empty_symlinks} = _mark_empty_symlinks($git_svn); + $self->{empty_symlinks} = + _mark_empty_symlinks($git_svn, $switch_path); } $self->{empty} = {}; $self->{dir_prop} = {}; @@ -3270,19 +3279,39 @@ sub new { # not inside them (when the Git::SVN::Fetcher object is passed) to # do_{switch,update} sub _mark_empty_symlinks { - my ($git_svn) = @_; + my ($git_svn, $switch_path) = @_; + my $bool = Git::config_bool('svn.brokenSymlinkWorkaround'); + return {} if (defined($bool) && ! $bool); + my %ret; my ($rev, $cmt) = $git_svn->last_rev_commit; return {} unless ($rev && $cmt); + # allow the warning to be printed for each revision we fetch to + # ensure the user sees it. The user can also disable the workaround + # on the repository even while git svn is running and the next + # revision fetched will skip this expensive function. + my $printed_warning; chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`); my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt); local $/ = "\0"; - my $pfx = $git_svn->{path}; + my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path}; $pfx .= '/' if length($pfx); while (<$ls>) { chomp; s/\A100644 blob $empty_blob\t//o or next; + unless ($printed_warning) { + print STDERR "Scanning for empty symlinks, ", + "this may take a while if you have ", + "many empty files\n", + "You may disable this with `", + "git config svn.brokenSymlinkWorkaround ", + "false'.\n", + "This may be done in a different ", + "terminal without restarting ", + "git svn\n"; + $printed_warning = 1; + } my $path = $_; my (undef, $props) = $git_svn->ra->get_file($pfx.$path, $rev, undef); @@ -4348,6 +4377,9 @@ sub gs_fetch_loop_common { } $self->get_log([$longest_path], $min, $max, 0, 1, 1, sub { $revs{$_[1]} = _cb(@_) }); + if ($err) { + print "Checked through r$max\r"; + } if ($err && $max >= $head) { print STDERR "Path '$longest_path' ", "was probably deleted:\n", diff --git a/git-web--browse.sh b/git-web--browse.sh index 78d236b77f..7ed0faddcd 100755 --- a/git-web--browse.sh +++ b/git-web--browse.sh @@ -115,7 +115,7 @@ if test -z "$browser" ; then browser_candidates="open $browser_candidates" fi # /bin/start indicates MinGW - if test -n /bin/start; then + if test -x /bin/start; then browser_candidates="start $browser_candidates" fi diff --git a/gitweb/README b/gitweb/README index a9dc2e57d9..8433dd1d45 100644 --- a/gitweb/README +++ b/gitweb/README @@ -212,6 +212,11 @@ not include variables usually directly set during build): Rename detection options for git-diff and git-diff-tree. By default ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or set it to () if you don't want to have renames detection. + * $prevent_xss + If true, some gitweb features are disabled to prevent content in + repositories from launching cross-site scripting (XSS) attacks. Set this + to true if you don't trust the content of your repositories. The default + is false. Projects list file format @@ -258,7 +263,9 @@ You can use the following files in repository: A .html file (HTML fragment) which is included on the gitweb project summary page inside <div> block element. You can use it for longer description of a project, to provide links (for example to project's - homepage), etc. + homepage), etc. This is recognized only if XSS prevention is off + ($prevent_xss is false); a way to include a readme safely when XSS + prevention is on may be worked out in the future. * description (or gitweb.description) Short (shortened by default to 25 characters in the projects list page) single line description of a project (of a repository). Plain text file; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index bec1af6b73..8dffa3fd53 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -132,6 +132,10 @@ our $fallback_encoding = 'latin1'; # - one might want to include '-B' option, e.g. '-B', '-M' our @diff_opts = ('-M'); # taken from git_commit +# Disables features that would allow repository owners to inject script into +# the gitweb domain. +our $prevent_xss = 0; + # information about snapshot formats that gitweb is capable of serving our %known_snapshot_formats = ( # name => { @@ -4503,7 +4507,9 @@ sub git_summary { print "</table>\n"; - if (-s "$projectroot/$project/README.html") { + # If XSS prevention is on, we don't include README.html. + # TODO: Allow a readme in some safe format. + if (!$prevent_xss && -s "$projectroot/$project/README.html") { print "<div class=\"title\">readme</div>\n" . "<div class=\"readme\">\n"; insert_file("$projectroot/$project/README.html"); @@ -4764,10 +4770,21 @@ sub git_blob_plain { $save_as .= '.txt'; } + # With XSS prevention on, blobs of all types except a few known safe + # ones are served with "Content-Disposition: attachment" to make sure + # they don't run in our security domain. For certain image types, + # blob view writes an <img> tag referring to blob_plain view, and we + # want to be sure not to break that by serving the image as an + # attachment (though Firefox 3 doesn't seem to care). + my $sandbox = $prevent_xss && + $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!; + print $cgi->header( -type => $type, -expires => $expires, - -content_disposition => 'inline; filename="' . $save_as . '"'); + -content_disposition => + ($sandbox ? 'attachment' : 'inline') + . '; filename="' . $save_as . '"'); undef $/; binmode STDOUT, ':raw'; print <$fd>; diff --git a/http-push.c b/http-push.c index a8ae545dfb..30d2d34041 100644 --- a/http-push.c +++ b/http-push.c @@ -153,6 +153,7 @@ struct remote_lock char *url; char *owner; char *token; + char tmpfile_suffix[41]; time_t start_time; long timeout; int refreshing; @@ -557,8 +558,7 @@ static void start_put(struct transfer_request *request) request->dest = strbuf_detach(&buf, NULL); append_remote_object_url(&buf, remote->url, hex, 0); - strbuf_addstr(&buf, "_"); - strbuf_addstr(&buf, request->lock->token); + strbuf_add(&buf, request->lock->tmpfile_suffix, 41); request->url = strbuf_detach(&buf, NULL); slot = get_active_slot(); @@ -1130,6 +1130,8 @@ static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) { struct remote_lock *lock = (struct remote_lock *)ctx->userData; + git_SHA_CTX sha_ctx; + unsigned char lock_token_sha1[20]; if (tag_closed && ctx->cdata) { if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) { @@ -1142,6 +1144,13 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { lock->token = xmalloc(strlen(ctx->cdata) + 1); strcpy(lock->token, ctx->cdata); + + git_SHA1_Init(&sha_ctx); + git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token)); + git_SHA1_Final(lock_token_sha1, &sha_ctx); + + lock->tmpfile_suffix[0] = '_'; + memcpy(lock->tmpfile_suffix + 1, sha1_to_hex(lock_token_sha1), 40); } } } diff --git a/log-tree.c b/log-tree.c index 194ddb13da..84a74e544b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -48,7 +48,7 @@ static void show_parents(struct commit *commit, int abbrev) struct commit_list *p; for (p = commit->parents; p ; p = p->next) { struct commit *parent = p->item; - printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev)); + printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev)); } } @@ -280,7 +280,7 @@ void show_log(struct rev_info *opt) putchar('>'); } } - fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); + fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); show_decorations(opt, commit); @@ -348,13 +348,13 @@ void show_log(struct rev_info *opt) putchar('>'); } } - fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), + fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); if (parent) printf(" (from %s)", - diff_unique_abbrev(parent->object.sha1, + find_unique_abbrev(parent->object.sha1, abbrev_commit)); show_decorations(opt, commit); printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET)); diff --git a/merge-recursive.c b/merge-recursive.c index b97026bd5c..ee853b990d 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -237,7 +237,7 @@ static int save_files_dirs(const unsigned char *sha1, string_list_insert(newpath, &o->current_file_set); free(newpath); - return READ_TREE_RECURSIVE; + return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } static int get_files_dirs(struct merge_options *o, struct tree *tree) diff --git a/notes.c b/notes.c deleted file mode 100644 index bd737842d9..0000000000 --- a/notes.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "cache.h" -#include "commit.h" -#include "notes.h" -#include "refs.h" -#include "utf8.h" -#include "strbuf.h" -#include "tree-walk.h" - -struct entry { - unsigned char commit_sha1[20]; - unsigned char notes_sha1[20]; -}; - -struct hash_map { - struct entry *entries; - off_t count, size; -}; - -static int initialized; -static struct hash_map hash_map; - -static int hash_index(struct hash_map *map, const unsigned char *sha1) -{ - int i = ((*(unsigned int *)sha1) % map->size); - - for (;;) { - unsigned char *current = map->entries[i].commit_sha1; - - if (!hashcmp(sha1, current)) - return i; - - if (is_null_sha1(current)) - return -1 - i; - - if (++i == map->size) - i = 0; - } -} - -static void add_entry(const unsigned char *commit_sha1, - const unsigned char *notes_sha1) -{ - int index; - - if (hash_map.count + 1 > hash_map.size >> 1) { - int i, old_size = hash_map.size; - struct entry *old = hash_map.entries; - - hash_map.size = old_size ? old_size << 1 : 64; - hash_map.entries = (struct entry *) - xcalloc(sizeof(struct entry), hash_map.size); - - for (i = 0; i < old_size; i++) - if (!is_null_sha1(old[i].commit_sha1)) { - index = -1 - hash_index(&hash_map, - old[i].commit_sha1); - memcpy(hash_map.entries + index, old + i, - sizeof(struct entry)); - } - free(old); - } - - index = hash_index(&hash_map, commit_sha1); - if (index < 0) { - index = -1 - index; - hash_map.count++; - } - - hashcpy(hash_map.entries[index].commit_sha1, commit_sha1); - hashcpy(hash_map.entries[index].notes_sha1, notes_sha1); -} - -static void initialize_hash_map(const char *notes_ref_name) -{ - unsigned char sha1[20], commit_sha1[20]; - unsigned mode; - struct tree_desc desc; - struct name_entry entry; - void *buf; - - if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) || - get_tree_entry(commit_sha1, "", sha1, &mode)) - return; - - buf = fill_tree_descriptor(&desc, sha1); - if (!buf) - die("Could not read %s for notes-index", sha1_to_hex(sha1)); - - while (tree_entry(&desc, &entry)) - if (!get_sha1(entry.path, commit_sha1)) - add_entry(commit_sha1, entry.sha1); - free(buf); -} - -static unsigned char *lookup_notes(const unsigned char *commit_sha1) -{ - int index; - - if (!hash_map.size) - return NULL; - - index = hash_index(&hash_map, commit_sha1); - if (index < 0) - return NULL; - return hash_map.entries[index].notes_sha1; -} - -void get_commit_notes(const struct commit *commit, struct strbuf *sb, - const char *output_encoding) -{ - static const char *utf8 = "utf-8"; - unsigned char *sha1; - char *msg, *msg_p; - unsigned long linelen, msglen; - enum object_type type; - - if (!initialized) { - const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT); - if (env) - notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT); - else if (!notes_ref_name) - notes_ref_name = GIT_NOTES_DEFAULT_REF; - initialize_hash_map(notes_ref_name); - initialized = 1; - } - - sha1 = lookup_notes(commit->object.sha1); - if (!sha1) - return; - - if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen || - type != OBJ_BLOB) - return; - - if (output_encoding && *output_encoding && - strcmp(utf8, output_encoding)) { - char *reencoded = reencode_string(msg, output_encoding, utf8); - if (reencoded) { - free(msg); - msg = reencoded; - msglen = strlen(msg); - } - } - - /* we will end the annotation by a newline anyway */ - if (msglen && msg[msglen - 1] == '\n') - msglen--; - - strbuf_addstr(sb, "\nNotes:\n"); - - for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) { - linelen = strchrnul(msg_p, '\n') - msg_p; - - strbuf_addstr(sb, " "); - strbuf_add(sb, msg_p, linelen); - strbuf_addch(sb, '\n'); - } - - free(msg); -} diff --git a/notes.h b/notes.h deleted file mode 100644 index 79d21b65f5..0000000000 --- a/notes.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NOTES_H -#define NOTES_H - -void get_commit_notes(const struct commit *commit, struct strbuf *sb, - const char *output_encoding); - -#endif @@ -154,7 +154,7 @@ int validate_headref(const char *path) /* Make sure it is a "refs/.." symlink */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); - if (len >= 11 && !memcmp("refs/heads/", buffer, 11)) + if (len >= 5 && !memcmp("refs/", buffer, 5)) return 0; return -1; } @@ -178,7 +178,7 @@ int validate_headref(const char *path) len -= 4; while (len && isspace(*buf)) buf++, len--; - if (len >= 11 && !memcmp("refs/heads/", buf, 11)) + if (len >= 5 && !memcmp("refs/", buf, 5)) return 0; } @@ -363,56 +363,97 @@ const char *make_relative_path(const char *abs, const char *base) } /* - * path = absolute path - * buf = buffer of at least max(2, strlen(path)+1) bytes - * It is okay if buf == path, but they should not overlap otherwise. + * It is okay if dst == src, but they should not overlap otherwise. * - * Performs the following normalizations on path, storing the result in buf: - * - Removes trailing slashes. - * - Removes empty components. + * Performs the following normalizations on src, storing the result in dst: + * - Ensures that components are separated by '/' (Windows only) + * - Squashes sequences of '/'. * - Removes "." components. * - Removes ".." components, and the components the precede them. - * "" and paths that contain only slashes are normalized to "/". - * Returns the length of the output. + * Returns failure (non-zero) if a ".." component appears as first path + * component anytime during the normalization. Otherwise, returns success (0). * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. */ -int normalize_absolute_path(char *buf, const char *path) +int normalize_path_copy(char *dst, const char *src) { - const char *comp_start = path, *comp_end = path; - char *dst = buf; - int comp_len; - assert(buf); - assert(path); - - while (*comp_start) { - assert(*comp_start == '/'); - while (*++comp_end && *comp_end != '/') - ; /* nothing */ - comp_len = comp_end - comp_start; - - if (!strncmp("/", comp_start, comp_len) || - !strncmp("/.", comp_start, comp_len)) - goto next; - - if (!strncmp("/..", comp_start, comp_len)) { - while (dst > buf && *--dst != '/') - ; /* nothing */ - goto next; - } + char *dst0; - memmove(dst, comp_start, comp_len); - dst += comp_len; - next: - comp_start = comp_end; + if (has_dos_drive_prefix(src)) { + *dst++ = *src++; + *dst++ = *src++; } + dst0 = dst; - if (dst == buf) + if (is_dir_sep(*src)) { *dst++ = '/'; + while (is_dir_sep(*src)) + src++; + } + + for (;;) { + char c = *src; + + /* + * A path component that begins with . could be + * special: + * (1) "." and ends -- ignore and terminate. + * (2) "./" -- ignore them, eat slash and continue. + * (3) ".." and ends -- strip one and terminate. + * (4) "../" -- strip one, eat slash and continue. + */ + if (c == '.') { + if (!src[1]) { + /* (1) */ + src++; + } else if (is_dir_sep(src[1])) { + /* (2) */ + src += 2; + while (is_dir_sep(*src)) + src++; + continue; + } else if (src[1] == '.') { + if (!src[2]) { + /* (3) */ + src += 2; + goto up_one; + } else if (is_dir_sep(src[2])) { + /* (4) */ + src += 3; + while (is_dir_sep(*src)) + src++; + goto up_one; + } + } + } + /* copy up to the next '/', and eat all '/' */ + while ((c = *src++) != '\0' && !is_dir_sep(c)) + *dst++ = c; + if (is_dir_sep(c)) { + *dst++ = '/'; + while (is_dir_sep(c)) + c = *src++; + src--; + } else if (!c) + break; + continue; + + up_one: + /* + * dst0..dst is prefix portion, and dst[-1] is '/'; + * go up one level. + */ + dst--; /* go to trailing '/' */ + if (dst <= dst0) + return -1; + /* Windows: dst[-1] cannot be backslash anymore */ + while (dst0 < dst && dst[-1] != '/') + dst--; + } *dst = '\0'; - return dst - buf; + return 0; } /* @@ -438,15 +479,16 @@ int longest_ancestor_length(const char *path, const char *prefix_list) return -1; for (colon = ceil = prefix_list; *colon; ceil = colon+1) { - for (colon = ceil; *colon && *colon != ':'; colon++); + for (colon = ceil; *colon && *colon != PATH_SEP; colon++); len = colon - ceil; if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) continue; strlcpy(buf, ceil, len+1); - len = normalize_absolute_path(buf, buf); - /* Strip "trailing slashes" from "/". */ - if (len == 1) - len = 0; + if (normalize_path_copy(buf, buf) < 0) + continue; + len = strlen(buf); + if (len > 0 && buf[len-1] == '/') + buf[--len] = '\0'; if (!strncmp(path, buf, len) && path[len] == '/' && @@ -6,7 +6,6 @@ #include "string-list.h" #include "mailmap.h" #include "log-tree.h" -#include "notes.h" #include "color.h" static char *user_format; @@ -211,15 +210,13 @@ static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, while (parent) { struct commit *p = parent->item; const char *hex = NULL; - const char *dots; if (abbrev) hex = find_unique_abbrev(p->object.sha1, abbrev); if (!hex) hex = sha1_to_hex(p->object.sha1); - dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; - strbuf_addf(sb, " %s%s", hex, dots); + strbuf_addf(sb, " %s", hex); } strbuf_addch(sb, '\n'); } @@ -921,9 +918,5 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, */ if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); - - if (fmt != CMIT_FMT_ONELINE) - get_commit_notes(commit, sb, encoding); - free(reencoded); } @@ -275,10 +275,8 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) list = get_ref_dir(ref, list); continue; } - if (!resolve_ref(ref, sha1, 1, &flag)) { - error("%s points nowhere!", ref); - continue; - } + if (!resolve_ref(ref, sha1, 1, &flag)) + hashclr(sha1); list = add_ref(ref, sha1, flag, list, NULL); } free(ref); @@ -287,6 +285,35 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) return sort_ref_list(list); } +struct warn_if_dangling_data { + const char *refname; + const char *msg_fmt; +}; + +static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1, + int flags, void *cb_data) +{ + struct warn_if_dangling_data *d = cb_data; + const char *resolves_to; + unsigned char junk[20]; + + if (!(flags & REF_ISSYMREF)) + return 0; + + resolves_to = resolve_ref(refname, junk, 0, NULL); + if (!resolves_to || strcmp(resolves_to, d->refname)) + return 0; + + printf(d->msg_fmt, refname); + return 0; +} + +void warn_dangling_symref(const char *msg_fmt, const char *refname) +{ + struct warn_if_dangling_data data = { refname, msg_fmt }; + for_each_rawref(warn_if_dangling_symref, &data); +} + static struct ref_list *get_loose_refs(void) { if (!cached_refs.did_loose) { @@ -498,16 +525,19 @@ int read_ref(const char *ref, unsigned char *sha1) return -1; } +#define DO_FOR_EACH_INCLUDE_BROKEN 01 static int do_one_ref(const char *base, each_ref_fn fn, int trim, - void *cb_data, struct ref_list *entry) + int flags, void *cb_data, struct ref_list *entry) { if (strncmp(base, entry->name, trim)) return 0; - if (is_null_sha1(entry->sha1)) - return 0; - if (!has_sha1_file(entry->sha1)) { - error("%s does not point to a valid object!", entry->name); - return 0; + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) { + if (is_null_sha1(entry->sha1)) + return 0; + if (!has_sha1_file(entry->sha1)) { + error("%s does not point to a valid object!", entry->name); + return 0; + } } current_ref = entry; return fn(entry->name + trim, entry->sha1, entry->flag, cb_data); @@ -561,7 +591,7 @@ fallback: } static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, - void *cb_data) + int flags, void *cb_data) { int retval = 0; struct ref_list *packed = get_packed_refs(); @@ -570,7 +600,7 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, struct ref_list *extra; for (extra = extra_refs; extra; extra = extra->next) - retval = do_one_ref(base, fn, trim, cb_data, extra); + retval = do_one_ref(base, fn, trim, flags, cb_data, extra); while (packed && loose) { struct ref_list *entry; @@ -586,13 +616,13 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, entry = packed; packed = packed->next; } - retval = do_one_ref(base, fn, trim, cb_data, entry); + retval = do_one_ref(base, fn, trim, flags, cb_data, entry); if (retval) goto end_each; } for (packed = packed ? packed : loose; packed; packed = packed->next) { - retval = do_one_ref(base, fn, trim, cb_data, packed); + retval = do_one_ref(base, fn, trim, flags, cb_data, packed); if (retval) goto end_each; } @@ -614,22 +644,28 @@ int head_ref(each_ref_fn fn, void *cb_data) int for_each_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/", fn, 0, cb_data); + return do_for_each_ref("refs/", fn, 0, 0, cb_data); } int for_each_tag_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/tags/", fn, 10, cb_data); + return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data); } int for_each_branch_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/heads/", fn, 11, cb_data); + return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data); } int for_each_remote_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/remotes/", fn, 13, cb_data); + return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data); +} + +int for_each_rawref(each_ref_fn fn, void *cb_data) +{ + return do_for_each_ref("refs/", fn, 0, + DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } /* @@ -24,6 +24,11 @@ extern int for_each_tag_ref(each_ref_fn, void *); extern int for_each_branch_ref(each_ref_fn, void *); extern int for_each_remote_ref(each_ref_fn, void *); +/* can be used to learn about broken ref and symref */ +extern int for_each_rawref(each_ref_fn, void *); + +extern void warn_dangling_symref(const char *msg_fmt, const char *refname); + /* * Extra refs will be listed by for_each_ref() before any actual refs * for the duration of this process or until clear_extra_refs() is @@ -290,7 +290,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) hex = xstrdup(sha1_to_hex(sha1)); string_list_insert(path, rr)->util = hex; if (mkdir(git_path("rr-cache/%s", hex), 0755)) - continue;; + continue; handle_file(path, NULL, rr_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } diff --git a/revision.c b/revision.c index 8603c14581..286e416b75 100644 --- a/revision.c +++ b/revision.c @@ -1738,14 +1738,16 @@ static struct commit *get_revision_1(struct rev_info *revs) (commit->date < revs->max_age)) continue; if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) - return NULL; + die("Failed to traverse parents of commit %s", + sha1_to_hex(commit->object.sha1)); } switch (simplify_commit(revs, commit)) { case commit_ignore: continue; case commit_error: - return NULL; + die("Failed to simplify parents of commit %s", + sha1_to_hex(commit->object.sha1)); default: return commit; } @@ -4,92 +4,6 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -static int sanitary_path_copy(char *dst, const char *src) -{ - char *dst0; - - if (has_dos_drive_prefix(src)) { - *dst++ = *src++; - *dst++ = *src++; - } - dst0 = dst; - - if (is_dir_sep(*src)) { - *dst++ = '/'; - while (is_dir_sep(*src)) - src++; - } - - for (;;) { - char c = *src; - - /* - * A path component that begins with . could be - * special: - * (1) "." and ends -- ignore and terminate. - * (2) "./" -- ignore them, eat slash and continue. - * (3) ".." and ends -- strip one and terminate. - * (4) "../" -- strip one, eat slash and continue. - */ - if (c == '.') { - if (!src[1]) { - /* (1) */ - src++; - } else if (is_dir_sep(src[1])) { - /* (2) */ - src += 2; - while (is_dir_sep(*src)) - src++; - continue; - } else if (src[1] == '.') { - if (!src[2]) { - /* (3) */ - src += 2; - goto up_one; - } else if (is_dir_sep(src[2])) { - /* (4) */ - src += 3; - while (is_dir_sep(*src)) - src++; - goto up_one; - } - } - } - - /* copy up to the next '/', and eat all '/' */ - while ((c = *src++) != '\0' && !is_dir_sep(c)) - *dst++ = c; - if (is_dir_sep(c)) { - *dst++ = '/'; - while (is_dir_sep(c)) - c = *src++; - src--; - } else if (!c) - break; - continue; - - up_one: - /* - * dst0..dst is prefix portion, and dst[-1] is '/'; - * go up one level. - */ - dst -= 2; /* go past trailing '/' if any */ - if (dst < dst0) - return -1; - while (1) { - if (dst <= dst0) - break; - c = *dst--; - if (c == '/') { /* MinGW: cannot be '\\' anymore */ - dst += 2; - break; - } - } - } - *dst = '\0'; - return 0; -} - const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; @@ -101,7 +15,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); } - if (sanitary_path_copy(sanitized, sanitized)) + if (normalize_path_copy(sanitized, sanitized)) goto error_out; if (is_absolute_path(orig)) { const char *work_tree = get_git_work_tree(); diff --git a/sha1_file.c b/sha1_file.c index 8868b800cb..5b6e0f61fa 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -689,6 +689,7 @@ void free_pack_by_name(const char *pack_name) while (*pp) { p = *pp; if (strcmp(pack_name, p->pack_name) == 0) { + clear_delta_base_cache(); close_pack_windows(p); if (p->pack_fd != -1) close(p->pack_fd); @@ -1663,6 +1664,13 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent) } } +void clear_delta_base_cache(void) +{ + unsigned long p; + for (p = 0; p < MAX_DELTA_CACHE; p++) + release_delta_base_cache(&delta_base_cache[p]); +} + static void add_delta_base_cache(struct packed_git *p, off_t base_offset, void *base, unsigned long base_size, enum object_type type) { diff --git a/sha1_name.c b/sha1_name.c index 5d0ac0263d..2f75179f4c 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -268,16 +268,19 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) char fullref[PATH_MAX]; unsigned char sha1_from_ref[20]; unsigned char *this_result; + int flag; this_result = refs_found ? sha1_from_ref : sha1; mksnpath(fullref, sizeof(fullref), *p, len, str); - r = resolve_ref(fullref, this_result, 1, NULL); + r = resolve_ref(fullref, this_result, 1, &flag); if (r) { if (!refs_found++) *ref = xstrdup(r); if (!warn_ambiguous_refs) break; - } + } else if ((flag & REF_ISSYMREF) && + (len != 4 || strcmp(str, "HEAD"))) + warning("ignoring dangling symref %s.", fullref); } free(last_branch); return refs_found; diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 6e7501f352..4ed1f0b4dd 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -8,36 +8,37 @@ test_description='Test various path utilities' . ./test-lib.sh norm_abs() { - test_expect_success "normalize absolute" \ - "test \$(test-path-utils normalize_absolute_path '$1') = '$2'" + test_expect_success "normalize absolute: $1 => $2" \ + "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'" } ancestor() { - test_expect_success "longest ancestor" \ - "test \$(test-path-utils longest_ancestor_length '$1' '$2') = '$3'" + test_expect_success "longest ancestor: $1 $2 => $3" \ + "test \"\$(test-path-utils longest_ancestor_length '$1' '$2')\" = '$3'" } -norm_abs "" / +norm_abs "" "" norm_abs / / norm_abs // / norm_abs /// / norm_abs /. / norm_abs /./ / -norm_abs /./.. / -norm_abs /../. / -norm_abs /./../.// / +norm_abs /./.. ++failed++ +norm_abs /../. ++failed++ +norm_abs /./../.// ++failed++ norm_abs /dir/.. / norm_abs /dir/sub/../.. / +norm_abs /dir/sub/../../.. ++failed++ norm_abs /dir /dir -norm_abs /dir// /dir +norm_abs /dir// /dir/ norm_abs /./dir /dir -norm_abs /dir/. /dir -norm_abs /dir///./ /dir -norm_abs /dir//sub/.. /dir -norm_abs /dir/sub/../ /dir -norm_abs //dir/sub/../. /dir -norm_abs /dir/s1/../s2/ /dir/s2 -norm_abs /d1/s1///s2/..//../s3/ /d1/s3 +norm_abs /dir/. /dir/ +norm_abs /dir///./ /dir/ +norm_abs /dir//sub/.. /dir/ +norm_abs /dir/sub/../ /dir/ +norm_abs //dir/sub/../. /dir/ +norm_abs /dir/s1/../s2/ /dir/s2/ +norm_abs /d1/s1///s2/..//../s3/ /d1/s3/ norm_abs /d1/s1//../s2/../../d2 /d2 norm_abs /d1/.../d2 /d1/.../d2 norm_abs /d1/..././../d2 /d1/d2 diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh new file mode 100755 index 0000000000..315b9b3f10 --- /dev/null +++ b/t/t0100-previous.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='previous branch syntax @{-n}' + +. ./test-lib.sh + +test_expect_success 'branch -d @{-1}' ' + test_commit A && + git checkout -b junk && + git checkout - && + test "$(git symbolic-ref HEAD)" = refs/heads/master && + git branch -d @{-1} && + test_must_fail git rev-parse --verify refs/heads/junk +' + +test_expect_success 'branch -d @{-12} when there is not enough switches yet' ' + git reflog expire --expire=now && + git checkout -b junk2 && + git checkout - && + test "$(git symbolic-ref HEAD)" = refs/heads/master && + test_must_fail git branch -d @{-12} && + git rev-parse --verify refs/heads/master +' + +test_expect_success 'merge @{-1}' ' + git checkout A && + test_commit B && + git checkout A && + test_commit C && + git branch -f master B && + git branch -f other && + git checkout other && + git checkout master && + git merge @{-1} && + git cat-file commit HEAD | grep "Merge branch '\''other'\''" +' + +test_expect_success 'merge @{-1} when there is not enough switches yet' ' + git reflog expire --expire=now && + git checkout -f master && + git reset --hard B && + git branch -f other C && + git checkout other && + git checkout master && + test_must_fail git merge @{-12} +' + +test_done + diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh index 569f34177d..7fa5f5b22a 100755 --- a/t/t1401-symbolic-ref.sh +++ b/t/t1401-symbolic-ref.sh @@ -27,11 +27,6 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' ' ' reset_to_sane -test_expect_success 'symbolic-ref refuses non-branch for HEAD' ' - test_must_fail git symbolic-ref HEAD refs/foo -' -reset_to_sane - test_expect_success 'symbolic-ref refuses bare sha1' ' echo content >file && git add file && git commit -m one test_must_fail git symbolic-ref HEAD `git rev-parse HEAD` diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 85da4caa7e..48ee07779d 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -26,21 +26,28 @@ test_rev_parse() { "test '$1' = \"\$(git rev-parse --show-prefix)\"" shift [ $# -eq 0 ] && return + + test_expect_success "$name: git-dir" \ + "test '$1' = \"\$(git rev-parse --git-dir)\"" + shift + [ $# -eq 0 ] && return } -# label is-bare is-inside-git is-inside-work prefix +# label is-bare is-inside-git is-inside-work prefix git-dir + +ROOT=$(pwd) -test_rev_parse toplevel false false true '' +test_rev_parse toplevel false false true '' .git cd .git || exit 1 -test_rev_parse .git/ false true false '' +test_rev_parse .git/ false true false '' . cd objects || exit 1 -test_rev_parse .git/objects/ false true false '' +test_rev_parse .git/objects/ false true false '' "$ROOT/.git" cd ../.. || exit 1 mkdir -p sub/dir || exit 1 cd sub/dir || exit 1 -test_rev_parse subdirectory false false true sub/dir/ +test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git" cd ../.. || exit 1 git config core.bare true diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index 27dc6c55d5..f6a6f839a1 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -92,13 +92,6 @@ cd sub/dir || exit 1 test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/ cd ../../../.. || exit 1 -test_expect_success 'detecting gitdir when cwd is in a subdir of gitdir' ' - (expected=$(pwd)/repo.git && - cd repo.git/refs && - unset GIT_DIR && - test "$expected" = "$(git rev-parse --git-dir)") -' - test_expect_success 'repo finds its work tree' ' (cd repo.git && : > work/sub/dir/untracked && diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh index 91b704a3a4..e377d48902 100755 --- a/t/t1504-ceiling-dirs.sh +++ b/t/t1504-ceiling-dirs.sh @@ -93,13 +93,13 @@ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi" test_prefix subdir_ceil_at_subdi_slash "sub/dir/" -GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub" +GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub" test_fail second_of_two -GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:bar" +GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:/bar" test_fail first_of_two -GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub:bar" +GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub:/bar" test_fail second_of_three diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh deleted file mode 100755 index 9393a25511..0000000000 --- a/t/t3301-notes.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2007 Johannes E. Schindelin -# - -test_description='Test commit notes' - -. ./test-lib.sh - -cat > fake_editor.sh << \EOF -echo "$MSG" > "$1" -echo "$MSG" >& 2 -EOF -chmod a+x fake_editor.sh -VISUAL=./fake_editor.sh -export VISUAL - -test_expect_success 'cannot annotate non-existing HEAD' ' - ! MSG=3 git notes edit -' - -test_expect_success setup ' - : > a1 && - git add a1 && - test_tick && - git commit -m 1st && - : > a2 && - git add a2 && - test_tick && - git commit -m 2nd -' - -test_expect_success 'need valid notes ref' ' - ! MSG=1 GIT_NOTES_REF='/' git notes edit && - ! MSG=2 GIT_NOTES_REF='/' git notes show -' - -test_expect_success 'create notes' ' - git config core.notesRef refs/notes/commits && - MSG=b1 git notes edit && - test ! -f .git/new-notes && - test 1 = $(git ls-tree refs/notes/commits | wc -l) && - test b1 = $(git notes show) && - git show HEAD^ && - ! git notes show HEAD^ -' - -cat > expect << EOF -commit 268048bfb8a1fb38e703baceb8ab235421bf80c5 -Author: A U Thor <author@example.com> -Date: Thu Apr 7 15:14:13 2005 -0700 - - 2nd - -Notes: - b1 -EOF - -test_expect_success 'show notes' ' - ! (git cat-file commit HEAD | grep b1) && - git log -1 > output && - test_cmp expect output -' -test_expect_success 'create multi-line notes (setup)' ' - : > a3 && - git add a3 && - test_tick && - git commit -m 3rd && - MSG="b3 -c3c3c3c3 -d3d3d3" git notes edit -' - -cat > expect-multiline << EOF -commit 1584215f1d29c65e99c6c6848626553fdd07fd75 -Author: A U Thor <author@example.com> -Date: Thu Apr 7 15:15:13 2005 -0700 - - 3rd - -Notes: - b3 - c3c3c3c3 - d3d3d3 -EOF - -printf "\n" >> expect-multiline -cat expect >> expect-multiline - -test_expect_success 'show multi-line notes' ' - git log -2 > output && - test_cmp expect-multiline output -' - -test_done diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh deleted file mode 100755 index 00d27bf6ef..0000000000 --- a/t/t3302-notes-index-expensive.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2007 Johannes E. Schindelin -# - -test_description='Test commit notes index (expensive!)' - -. ./test-lib.sh - -test -z "$GIT_NOTES_TIMING_TESTS" && { - say Skipping timing tests - test_done - exit -} - -create_repo () { - number_of_commits=$1 - nr=0 - parent= - test -d .git || { - git init && - tree=$(git write-tree) && - while [ $nr -lt $number_of_commits ]; do - test_tick && - commit=$(echo $nr | git commit-tree $tree $parent) || - return - parent="-p $commit" - nr=$(($nr+1)) - done && - git update-ref refs/heads/master $commit && - { - export GIT_INDEX_FILE=.git/temp; - git rev-list HEAD | cat -n | sed "s/^[ ][ ]*/ /g" | - while read nr sha1; do - blob=$(echo note $nr | git hash-object -w --stdin) && - echo $sha1 | sed "s/^/0644 $blob 0 /" - done | git update-index --index-info && - tree=$(git write-tree) && - test_tick && - commit=$(echo notes | git commit-tree $tree) && - git update-ref refs/notes/commits $commit - } && - git config core.notesRef refs/notes/commits - } -} - -test_notes () { - count=$1 && - git config core.notesRef refs/notes/commits && - git log | grep "^ " > output && - i=1 && - while [ $i -le $count ]; do - echo " $(($count-$i))" && - echo " note $i" && - i=$(($i+1)); - done > expect && - git diff expect output -} - -cat > time_notes << \EOF - mode=$1 - i=1 - while [ $i -lt $2 ]; do - case $1 in - no-notes) - export GIT_NOTES_REF=non-existing - ;; - notes) - unset GIT_NOTES_REF - ;; - esac - git log >/dev/null - i=$(($i+1)) - done -EOF - -time_notes () { - for mode in no-notes notes - do - echo $mode - /usr/bin/time sh ../time_notes $mode $1 - done -} - -for count in 10 100 1000 10000; do - - mkdir $count - (cd $count; - - test_expect_success "setup $count" "create_repo $count" - - test_expect_success 'notes work' "test_notes $count" - - test_expect_success 'notes timing' "time_notes 100" - ) -done - -test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index b7a670ef40..8c0c5f5982 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -14,7 +14,8 @@ export GIT_AUTHOR_EMAIL test_expect_success \ 'prepare repository with topic branches' \ - 'echo First > A && + 'git config core.logAllRefUpdates true && + echo First > A && git update-index --add A && git commit -m "Add A." && git checkout -b my-topic-branch && @@ -84,4 +85,14 @@ test_expect_success 'rebase a single mode change' ' GIT_TRACE=1 git rebase master ' +test_expect_success 'HEAD was detached during rebase' ' + test $(git rev-parse HEAD@{1}) != $(git rev-parse modechange@{1}) +' + +test_expect_success 'Show verbose error when HEAD could not be detached' ' + : > B && + test_must_fail git rebase topic 2> output.err > output.out && + grep "Untracked working tree file .B. would be overwritten" output.err +' + test_done diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ index 3ceb8e73c5..bd7f5c0f70 100644 --- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ @@ -1,6 +1,6 @@ $ git log --patch-with-stat --summary master -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master index 43d77761f9..14595a614c 100644 --- a/t/t4013/diff.log_--patch-with-stat_master +++ b/t/t4013/diff.log_--patch-with-stat_master @@ -1,6 +1,6 @@ $ git log --patch-with-stat master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ index 5187a26816..5a4e72765d 100644 --- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ @@ -1,6 +1,6 @@ $ git log --patch-with-stat master -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master index c9640976a8..df0aaa9f2c 100644 --- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root --cc --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master index ad050af55f..c11b5f2c7f 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master index 628c6c03bc..5f0c98f9ce 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_master @@ -1,6 +1,6 @@ $ git log --root --patch-with-stat master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master index 5d4e0f13b5..e62c368dc6 100644 --- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root -c --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master index 217a2eb203..b42c334439 100644 --- a/t/t4013/diff.log_--root_-p_master +++ b/t/t4013/diff.log_--root_-p_master @@ -1,6 +1,6 @@ $ git log --root -p master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master index e17ccfc234..e8f46159da 100644 --- a/t/t4013/diff.log_--root_master +++ b/t/t4013/diff.log_--root_master @@ -1,6 +1,6 @@ $ git log --root master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master index f8fefef2c3..bf1326dc36 100644 --- a/t/t4013/diff.log_-p_master +++ b/t/t4013/diff.log_-p_master @@ -1,6 +1,6 @@ $ git log -p master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master index e9d9e7b40a..a8f6ce5abd 100644 --- a/t/t4013/diff.log_master +++ b/t/t4013/diff.log_master @@ -1,6 +1,6 @@ $ git log master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master index 9e6e1f2710..fb08ce0e46 100644 --- a/t/t4013/diff.show_master +++ b/t/t4013/diff.show_master @@ -1,6 +1,6 @@ $ git show master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master index 5facf2543d..e96ff1fb8c 100644 --- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git whatchanged --root --cc --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master index 10f6767e49..c0aff68ef6 100644 --- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git whatchanged --root -c --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index caea292f15..281680d95a 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -128,4 +128,12 @@ test_expect_success 'force diff with "diff"' ' test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual ' +test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' ' + echo anotherfile > file2 && + git add file2 && + git commit -m "added 2nd file" && + echo modified >file2 && + GIT_EXTERNAL_DIFF=echo git diff +' + test_done diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 771c0a06a4..55ed7c7935 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -112,4 +112,42 @@ test_expect_success 'prune: do not prune heads listed as an argument' ' ' +test_expect_success 'gc --no-prune' ' + + before=$(git count-objects | sed "s/ .*//") && + BLOB=$(echo aleph_0 | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime =-$((86400*5001)) $BLOB_FILE && + git config gc.pruneExpire 2.days.ago && + git gc --no-prune && + test 1 = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE + +' + +test_expect_success 'gc respects gc.pruneExpire' ' + + git config gc.pruneExpire 5002.days.ago && + git gc && + test -f $BLOB_FILE && + git config gc.pruneExpire 5000.days.ago && + git gc && + test ! -f $BLOB_FILE + +' + +test_expect_success 'gc --prune=<date>' ' + + BLOB=$(echo aleph_0 | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test-chmtime =-$((86400*5001)) $BLOB_FILE && + git gc --prune=5002.days.ago && + test -f $BLOB_FILE && + git gc --prune=5000.days.ago && + test ! -f $BLOB_FILE + +' + test_done diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh new file mode 100755 index 0000000000..ae52a1882d --- /dev/null +++ b/t/t5307-pack-missing-commit.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +test_description='pack should notice missing commit objects' + +. ./test-lib.sh + +test_expect_success setup ' + for i in 1 2 3 4 5 + do + echo "$i" >"file$i" && + git add "file$i" && + test_tick && + git commit -m "$i" && + git tag "tag$i" + done && + obj=$(git rev-parse --verify tag3) && + fanout=$(expr "$obj" : "\(..\)") && + remainder=$(expr "$obj" : "..\(.*\)") && + rm -f ".git/objects/$fanout/$remainder" +' + +test_expect_success 'check corruption' ' + test_must_fail git fsck +' + +test_expect_success 'rev-list notices corruption (1)' ' + test_must_fail git rev-list HEAD +' + +test_expect_success 'rev-list notices corruption (2)' ' + test_must_fail git rev-list --objects HEAD +' + +test_expect_success 'pack-objects notices corruption' ' + echo HEAD | + test_must_fail git pack-objects --revs pack +' + +test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index b21317d685..f2d5581b12 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -32,9 +32,7 @@ test_expect_success setup ' done && git update-ref HEAD "$commit" && git clone ./. victim && - cd victim && - git log && - cd .. && + ( cd victim && git log ) && git update-ref HEAD "$zero" && parent=$zero && i=0 && @@ -59,88 +57,84 @@ test_expect_success 'pack the source repository' ' ' test_expect_success 'pack the destination repository' ' + ( cd victim && git repack -a -d && - git prune && - cd .. + git prune + ) ' -test_expect_success \ - 'pushing rewound head should not barf but require --force' ' - # should not fail but refuse to update. - if git send-pack ./victim/.git/ master - then - # now it should fail with Pasky patch - echo >&2 Gaah, it should have failed. - false - else - echo >&2 Thanks, it correctly failed. - true - fi && - if cmp victim/.git/refs/heads/master .git/refs/heads/master - then - # should have been left as it was! - false - else - true - fi && +test_expect_success 'refuse pushing rewound head without --force' ' + pushed_head=$(git rev-parse --verify master) && + victim_orig=$(cd victim && git rev-parse --verify master) && + test_must_fail git send-pack ./victim master && + victim_head=$(cd victim && git rev-parse --verify master) && + test "$victim_head" = "$victim_orig" && # this should update - git send-pack --force ./victim/.git/ master && - cmp victim/.git/refs/heads/master .git/refs/heads/master + git send-pack --force ./victim master && + victim_head=$(cd victim && git rev-parse --verify master) && + test "$victim_head" = "$pushed_head" ' test_expect_success \ 'push can be used to delete a ref' ' - cd victim && - git branch extra master && - cd .. && - test -f victim/.git/refs/heads/extra && - git send-pack ./victim/.git/ :extra master && - ! test -f victim/.git/refs/heads/extra + ( cd victim && git branch extra master ) && + git send-pack ./victim :extra master && + ( cd victim && + test_must_fail git rev-parse --verify extra ) ' -unset GIT_CONFIG -HOME=`pwd`/no-such-directory -export HOME ;# this way we force the victim/.git/config to be used. - -test_expect_success \ - 'pushing a delete should be denied with denyDeletes' ' - cd victim && - git config receive.denyDeletes true && - git branch extra master && - cd .. && - test -f victim/.git/refs/heads/extra && - test_must_fail git send-pack ./victim/.git/ :extra master +test_expect_success 'refuse deleting push with denyDeletes' ' + ( + cd victim && + ( git branch -D extra || : ) && + git config receive.denyDeletes true && + git branch extra master + ) && + test_must_fail git send-pack ./victim :extra master ' -rm -f victim/.git/refs/heads/extra -test_expect_success \ - 'pushing with --force should be denied with denyNonFastforwards' ' - cd victim && - git config receive.denyNonFastforwards true && - cd .. && - git update-ref refs/heads/master master^ || return 1 - git send-pack --force ./victim/.git/ master && return 1 - ! test_cmp .git/refs/heads/master victim/.git/refs/heads/master +test_expect_success 'denyNonFastforwards trumps --force' ' + ( + cd victim && + ( git branch -D extra || : ) && + git config receive.denyNonFastforwards true + ) && + victim_orig=$(cd victim && git rev-parse --verify master) && + test_must_fail git send-pack --force ./victim master^:master && + victim_head=$(cd victim && git rev-parse --verify master) && + test "$victim_orig" = "$victim_head" ' -test_expect_success \ - 'pushing does not include non-head refs' ' - mkdir parent && cd parent && - git init && touch file && git add file && git commit -m add && - cd .. && - git clone parent child && cd child && git push --all && - cd ../parent && - git branch -a >branches && ! grep origin/master branches +test_expect_success 'push --all excludes remote tracking hierarchy' ' + mkdir parent && + ( + cd parent && + git init && : >file && git add file && git commit -m add + ) && + git clone parent child && + ( + cd child && git push --all + ) && + ( + cd parent && + test -z "$(git for-each-ref refs/remotes/origin)" + ) ' rewound_push_setup() { rm -rf parent child && - mkdir parent && cd parent && - git init && echo one >file && git add file && git commit -m one && - echo two >file && git commit -a -m two && - cd .. && - git clone parent child && cd child && git reset --hard HEAD^ + mkdir parent && + ( + cd parent && + git init && + echo one >file && git add file && git commit -m one && + echo two >file && git commit -a -m two + ) && + git clone parent child && + ( + cd child && git reset --hard HEAD^ + ) } rewound_push_succeeded() { @@ -156,30 +150,57 @@ rewound_push_failed() { fi } -test_expect_success \ - 'pushing explicit refspecs respects forcing' ' +test_expect_success 'pushing explicit refspecs respects forcing' ' rewound_push_setup && - if git send-pack ../parent/.git refs/heads/master:refs/heads/master - then - false - else - true - fi && rewound_push_failed && - git send-pack ../parent/.git +refs/heads/master:refs/heads/master && - rewound_push_succeeded + parent_orig=$(cd parent && git rev-parse --verify master) && + ( + cd child && + test_must_fail git send-pack ../parent \ + refs/heads/master:refs/heads/master + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + test "$parent_orig" = "$parent_head" && + ( + cd child && + git send-pack ../parent \ + +refs/heads/master:refs/heads/master + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + child_head=$(cd parent && git rev-parse --verify master) && + test "$parent_head" = "$child_head" ' -test_expect_success \ - 'pushing wildcard refspecs respects forcing' ' +test_expect_success 'pushing wildcard refspecs respects forcing' ' rewound_push_setup && - if git send-pack ../parent/.git refs/heads/*:refs/heads/* - then - false - else - true - fi && rewound_push_failed && - git send-pack ../parent/.git +refs/heads/*:refs/heads/* && - rewound_push_succeeded + parent_orig=$(cd parent && git rev-parse --verify master) && + ( + cd child && + test_must_fail git send-pack ../parent \ + "refs/heads/*:refs/heads/*" + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + test "$parent_orig" = "$parent_head" && + ( + cd child && + git send-pack ../parent \ + "+refs/heads/*:refs/heads/*" + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + child_head=$(cd parent && git rev-parse --verify master) && + test "$parent_head" = "$child_head" +' + +test_expect_success 'warn pushing to delete current branch' ' + rewound_push_setup && + ( + cd child && + git send-pack ../parent :refs/heads/master 2>errs + ) && + grep "warning: to refuse deleting" child/errs && + ( + cd parent && + test_must_fail git rev-parse --verify master + ) ' test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index bc5b7ce4a6..eb637184a0 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -402,4 +402,31 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' ' test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin") ' +test_expect_success 'remote prune to cause a dangling symref' ' + git clone one seven && + ( + cd one && + git checkout side2 && + git branch -D master + ) && + ( + cd seven && + git remote prune origin + ) 2>err && + grep "has become dangling" err && + + : And the dangling symref will not cause other annoying errors + ( + cd seven && + git branch -a + ) 2>err && + ! grep "points nowhere" err + ( + cd seven && + test_must_fail git branch nomore origin + ) 2>err && + grep "dangling symref" err +' + test_done + diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index c236b5e83b..11b343274f 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -94,6 +94,13 @@ test_expect_success 'MKCOL sends directory names with trailing slashes' ' ' +test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' ' + + grep -P "\"(?:PUT|MOVE) .+objects/[\da-z]{2}/[\da-z]{38}_[\da-z\-]{40} HTTP/[0-9.]+\" 20\d" \ + < "$HTTPD_ROOT_PATH"/access.log + +' + stop_httpd test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index fe287d31fb..44793f2eee 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -144,4 +144,19 @@ test_expect_success 'clone to an existing path' ' test_must_fail git clone src target-5 ' +test_expect_success 'clone a void' ' + mkdir src-0 && + ( + cd src-0 && git init + ) && + git clone src-0 target-6 && + ( + cd src-0 && test_commit A + ) && + git clone src-0 target-7 && + # There is no reason to insist they are bit-for-bit + # identical, but this test should suffice for now. + test_cmp target-6/.git/config target-7/.git/config +' + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index cb0474336d..56b5eccdb4 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -48,6 +48,10 @@ test_expect_success 'result is really identical' ' test $H = $(git rev-parse HEAD) ' +test_expect_success 'Fail if commit filter fails' ' + test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD +' + test_expect_success 'rewrite, renaming a specific file' ' git filter-branch -f --tree-filter "mv d doh || :" HEAD ' diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 2ec7ac6a51..b8cb2df667 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -234,4 +234,17 @@ test_expect_success 'gracefully add submodule with a trailing slash' ' ' +test_expect_success 'ls-files gracefully handles trailing slash' ' + + test "init" = "$(git ls-files init/)" + +' + +test_expect_success 'submodule <invalid-path> warns' ' + + git submodule no-such-submodule 2> output.err && + grep "^error: .*no-such-submodule" output.err + +' + test_done diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh index 704a4f8574..20529a878c 100755 --- a/t/t9131-git-svn-empty-symlink.sh +++ b/t/t9131-git-svn-empty-symlink.sh @@ -87,4 +87,14 @@ test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar' test_expect_success 'get "bar" => symlink fix from svn' \ '(cd x && git svn rebase)' test_expect_success '"bar" becomes a symlink' 'test -L x/bar' + + +test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y' +test_expect_success 'disable broken symlink workaround' \ + '(cd y && git config svn.brokenSymlinkWorkaround false)' +test_expect_success '"bar" is an empty file' 'test -f y/bar && ! test -s y/bar' +test_expect_success 'get "bar" => symlink fix from svn' \ + '(cd y && git svn rebase)' +test_expect_success '"bar" does not become a symlink' '! test -L y/bar' + test_done diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh new file mode 100755 index 0000000000..03705fa4ce --- /dev/null +++ b/t/t9135-git-svn-moved-branch-empty-file.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +test_description='test moved svn branch with missing empty files' + +. ./lib-git-svn.sh +test_expect_success 'load svn dumpfile' ' + svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9135/svn.dump" + ' + +test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x' + +test_expect_success 'test that b1 exists and is empty' ' + (cd x && test -f b1 && ! test -s b1) + ' + +test_done diff --git a/t/t9135/svn.dump b/t/t9135/svn.dump new file mode 100644 index 0000000000..b51c0ccceb --- /dev/null +++ b/t/t9135/svn.dump @@ -0,0 +1,192 @@ +SVN-fs-dump-format-version: 2 + +UUID: 1f80e919-e9e3-4d80-a3ae-d9f21095e27b + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2009-02-10T19:23:16.424027Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 123 +Content-length: 123 + +K 7 +svn:log +V 20 +init standard layout +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:17.195072Z +PROPS-END + +Node-path: branches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 121 +Content-length: 121 + +K 7 +svn:log +V 18 +branch-b off trunk +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:19.160095Z +PROPS-END + +Node-path: branches/branch-b +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk +Prop-content-length: 34 +Content-length: 34 + +K 13 +svn:mergeinfo +V 0 + +PROPS-END + + +Revision-number: 3 +Prop-content-length: 120 +Content-length: 120 + +K 7 +svn:log +V 17 +add empty file b1 +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:20.194568Z +PROPS-END + +Node-path: branches/branch-b/b1 +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 0 +Text-content-md5: d41d8cd98f00b204e9800998ecf8427e +Content-length: 10 + +PROPS-END + + +Revision-number: 4 +Prop-content-length: 110 +Content-length: 110 + +K 7 +svn:log +V 8 +branch-c +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:21.169100Z +PROPS-END + +Node-path: branches/branch-c +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 3 +Node-copyfrom-path: trunk + + +Revision-number: 5 +Prop-content-length: 126 +Content-length: 126 + +K 7 +svn:log +V 23 +oops, wrong branchpoint +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:21.253557Z +PROPS-END + +Node-path: branches/branch-c +Node-action: delete + + +Revision-number: 6 +Prop-content-length: 127 +Content-length: 127 + +K 7 +svn:log +V 24 +branch-c off of branch-b +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-10T19:23:21.314659Z +PROPS-END + +Node-path: branches/branch-c +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 5 +Node-copyfrom-path: branches/branch-b +Prop-content-length: 34 +Content-length: 34 + +K 13 +svn:mergeinfo +V 0 + +PROPS-END + + diff --git a/test-path-utils.c b/test-path-utils.c index 2c0f5a37e8..5168a8e3df 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -2,11 +2,13 @@ int main(int argc, char **argv) { - if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) { + if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { char *buf = xmalloc(PATH_MAX + 1); - int rv = normalize_absolute_path(buf, argv[2]); - assert(strlen(buf) == rv); + int rv = normalize_path_copy(buf, argv[2]); + if (rv) + buf = "++failed++"; puts(buf); + return 0; } if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) { @@ -15,12 +17,16 @@ int main(int argc, char **argv) argc--; argv++; } + return 0; } if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { int len = longest_ancestor_length(argv[2], argv[3]); printf("%d\n", len); + return 0; } - return 0; + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], + argv[1] ? argv[1] : "(there was none)"); + return 1; } @@ -110,7 +110,7 @@ int read_tree_recursive(struct tree *tree, case 0: continue; case READ_TREE_RECURSIVE: - break;; + break; default: return -1; } @@ -131,6 +131,34 @@ int read_tree_recursive(struct tree *tree, if (retval) return -1; continue; + } else if (S_ISGITLINK(entry.mode)) { + int retval; + struct strbuf path; + unsigned int entrylen; + struct commit *commit; + + entrylen = tree_entry_len(entry.path, entry.sha1); + strbuf_init(&path, baselen + entrylen + 1); + strbuf_add(&path, base, baselen); + strbuf_add(&path, entry.path, entrylen); + strbuf_addch(&path, '/'); + + commit = lookup_commit(entry.sha1); + if (!commit) + die("Commit %s in submodule path %s not found", + sha1_to_hex(entry.sha1), path.buf); + + if (parse_commit(commit)) + die("Invalid commit %s in submodule path %s", + sha1_to_hex(entry.sha1), path.buf); + + retval = read_tree_recursive(commit->tree, + path.buf, path.len, + stage, match, fn, context); + strbuf_release(&path); + if (retval) + return -1; + continue; } } return 0; @@ -18,7 +18,7 @@ void walker_say(struct walker *walker, const char *fmt, const char *hex) static void report_missing(const struct object *obj) { char missing_hex[41]; - strcpy(missing_hex, sha1_to_hex(obj->sha1));; + strcpy(missing_hex, sha1_to_hex(obj->sha1)); fprintf(stderr, "Cannot obtain needed %s %s\n", obj->type ? typename(obj->type): "object", missing_hex); if (!is_null_sha1(current_commit_sha1)) |