Merge branch 'tr/add-p-single'
* tr/add-p-single:
add -p: import Term::ReadKey with 'require'
add -p: print errors in separate color
add -p: prompt for single characters
diff --git a/.gitignore b/.gitignore
index 1c57d4c..13311f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,6 +82,7 @@
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.3.txt b/Documentation/RelNotes-1.6.1.3.txt
new file mode 100644
index 0000000..6f0bde1
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.3.txt
@@ -0,0 +1,32 @@
+GIT v1.6.1.3 Release Notes
+==========================
+
+Fixes since v1.6.1.2
+--------------------
+
+* "git diff --binary | git apply" pipeline did not work well when
+ a binary blob is changed to a symbolic link.
+
+* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
+ not work as expected.
+
+* "git grep" did not pass the -I (ignore binary) option when
+ calling out an external grep program.
+
+* "git log" and friends include HEAD to the set of starting points
+ when --all is given. This makes a difference when you are not
+ on any branch.
+
+* "git mv" to move an untracked file to overwrite a tracked
+ contents misbehaved.
+
+* "git merge -s octopus" with many potential merge bases did not
+ work correctly.
+
+* RPM binary package installed the html manpages in a wrong place.
+
+Also includes minor documentation fixes and updates.
+
+
+--
+git shortlog --no-merges v1.6.1.2-33-gc789350..
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 1806a60..1dd18c9 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -422,6 +422,19 @@
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-am.txt b/Documentation/git-am.txt
index efd311b..ff307eb 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -10,7 +10,8 @@
--------
[verse]
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
- [--3way] [--interactive]
+ [--3way] [--interactive] [--committer-date-is-author-date]
+ [--ignore-date]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
[--reject]
[<mbox> | <Maildir>...]
@@ -73,6 +74,20 @@
--interactive::
Run interactively.
+--committer-date-is-author-date::
+ By default the command records the date from the e-mail
+ message as the commit author date, and uses the time of
+ commit creation as the committer date. This allows the
+ user to lie about the committer date by using the same
+ timestamp as the author date.
+
+--ignore-date::
+ By default the command records the date from the e-mail
+ message as the commit author date, and uses the time of
+ commit creation as the committer date. This allows the
+ user to lie about author timestamp by using the same
+ timestamp as the committer date.
+
--skip::
Skip the current patch. This is only meaningful when
restarting an aborted patch.
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index 1b66ab7..ea0f6a0 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -84,7 +84,7 @@
than one basis can be specified. The objects packaged are those not
contained in the union of the given bases. Each basis can be
specified explicitly (e.g., ^master~10), or implicitly (e.g.,
-master~10..master, master --since=10.days.ago).
+master~10..master, --since=10.days.ago master).
It is very important that the basis used be held by the destination.
It is okay to err on the side of conservatism, causing the bundle file
@@ -94,75 +94,111 @@
EXAMPLE
-------
-Assume two repositories exist as R1 on machine A, and R2 on machine B.
+Assume you want to transfer the history from a repository R1 on machine A
+to another repository R2 on machine B.
For whatever reason, direct connection between A and B is not allowed,
but we can move data from A to B via some mechanism (CD, email, etc).
We want to update R2 with developments made on branch master in R1.
-To create the bundle you have to specify the basis. You have some options:
+To bootstrap the process, you can first create a bundle that doesn't have
+any basis. You can use a tag to remember up to what commit you sent out
+in order to make it easy to later update the other repository with
+incremental bundle,
-- Without basis.
-+
-This is useful when sending the whole history.
+----------------
+machineA$ cd R1
+machineA$ git bundle create file.bdl master
+machineA$ git tag -f lastR2bundle master
+----------------
-------------
-$ git bundle create mybundle master
-------------
+Then you sneakernet file.bdl 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.
-- Using temporally tags.
-+
-We set a tag in R1 (lastR2bundle) after the previous such transport,
-and move it afterwards to help build the bundle.
+----------------
+machineB$ git clone /home/me/tmp/file.bdl R2
+----------------
-------------
-$ git bundle create mybundle master ^lastR2bundle
-$ git tag -f lastR2bundle master
-------------
-
-- Using a tag present in both repositories
-
-------------
-$ git bundle create mybundle master ^v1.0.0
-------------
-
-- A basis based on time.
-
-------------
-$ git bundle create mybundle master --since=10.days.ago
-------------
-
-- With a limit on the number of commits
-
-------------
-$ git bundle create mybundle master -n 10
-------------
-
-Then you move mybundle from A to B, and in R2 on B:
-
-------------
-$ git bundle verify mybundle
-$ git fetch mybundle master:localRef
-------------
-
-With something like this in the config in R2:
+This will define a remote called "origin" in the resulting repository that
+lets you fetch and pull from the bundle. $GIT_DIR/config file in R2 may
+have an entry like this:
------------------------
-[remote "bundle"]
+[remote "origin"]
url = /home/me/tmp/file.bdl
fetch = refs/heads/*:refs/remotes/origin/*
------------------------
-You can first sneakernet the bundle file to ~/tmp/file.bdl and
-then these commands on machine B:
+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
+updates from here on.
-------------
-$ git ls-remote bundle
-$ git fetch bundle
-$ git pull bundle
-------------
+After working more in the original repository, you can create an
+incremental bundle to update the other:
-would treat it as if it is talking with a remote side over the
-network.
+----------------
+machineA$ cd R1
+machineA$ git bundle create file.bdl lastR2bundle..master
+machineA$ git tag -f lastR2bundle master
+----------------
+
+and sneakernet it to the other machine to replace /home/me/tmp/file.bdl,
+and pull from it.
+
+----------------
+machineB$ cd R2
+machineB$ git pull
+----------------
+
+If you know up to what commit the intended recipient repository should
+have the necessary objects for, you can use that knowledge to specify the
+basis, giving a cut-off point to limit the revisions and objects that go
+in the resulting bundle. The previous example used lastR2bundle tag
+for this purpose, but you can use other options you would give to
+the linkgit:git-log[1] command. Here are more examples:
+
+You can use a tag that is present in both.
+
+----------------
+$ git bundle create mybundle v1.0.0..master
+----------------
+
+You can use a basis based on time.
+
+----------------
+$ git bundle create mybundle --since=10.days master
+----------------
+
+Or you can use the number of commits.
+
+----------------
+$ git bundle create mybundle -10 master
+----------------
+
+You can run `git-bundle verify` to see if you can extract from a bundle
+that was created with a basis.
+
+----------------
+$ git bundle verify mybundle
+----------------
+
+This will list what commits you must have in order to extract from the
+bundle and will error out if you don't have them.
+
+A bundle from a recipient repository's point of view is just like a
+regular repository it fetches/pulls from. You can for example map
+refs, like this example, when fetching:
+
+----------------
+$ git fetch mybundle master:localRef
+----------------
+
+Or see what refs it offers.
+
+----------------
+$ git ls-remote mybundle
+----------------
Author
------
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
new file mode 100644
index 0000000..3d93625
--- /dev/null
+++ b/Documentation/git-notes.txt
@@ -0,0 +1,46 @@
+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-show-branch.txt b/Documentation/git-show-branch.txt
index 8277577..7e9ff37 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -99,12 +99,12 @@
will show the revisions given by "git rev-list {caret}master
topic1 topic2"
+-g::
--reflog[=<n>[,<base>]] [<ref>]::
Shows <n> most recent ref-log entries for the given
ref. If <base> is given, <n> entries going back from
that entry. <base> can be specified as count or date.
- `-g` can be used as a short-hand for this option. When
- no explicit <ref> parameter is given, it defaults to the
+ When no explicit <ref> parameter is given, it defaults to the
current branch (or `HEAD` if it is detached).
Note that --more, --list, --independent and --merge-base options
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index 9a4e3ea..ac56d1c 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -21,7 +21,7 @@
buffer from its strbuf shell in a safe way. That is the sole supported
way. This will give you a malloced buffer that you can later `free()`.
+
-However, it it totally safe to modify anything in the string pointed by
+However, it is totally safe to modify anything in the string pointed by
the `buf` member, between the indices `0` and `len-1` (inclusive).
. The `buf` member is a byte array that has at least `len + 1` bytes
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index fa34c67..5355ebc 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -6,10 +6,10 @@
===============================================================
- rsync://host.xz/path/to/repo.git/
-- http://host.xz/path/to/repo.git/
-- https://host.xz/path/to/repo.git/
-- git://host.xz/path/to/repo.git/
-- git://host.xz/~user/path/to/repo.git/
+- http://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- https://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/~user/path/to/repo.git/
- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 19f571a..96af8977 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1507,7 +1507,7 @@
work-in-progress changes.
------------------------------------------------
-$ git stash "work in progress for foo feature"
+$ git stash save "work in progress for foo feature"
------------------------------------------------
This command will save your changes away to the `stash`, and
diff --git a/Makefile b/Makefile
index 9f3a8ab..27b9569 100644
--- a/Makefile
+++ b/Makefile
@@ -265,6 +265,7 @@
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
@@ -377,6 +378,7 @@
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
@@ -459,6 +461,7 @@
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
@@ -649,10 +652,12 @@
ifeq ($(uname_S),Darwin)
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
- ifneq ($(shell expr "$(uname_R)" : '9\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
OLD_ICONV = UnfortunatelyYes
endif
- NO_STRLCPY = YesPlease
+ ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
+ NO_STRLCPY = YesPlease
+ endif
NO_MEMMEM = YesPlease
THREADED_DELTA_SEARCH = YesPlease
endif
@@ -1382,6 +1387,7 @@
TEST_PROGRAMS += test-ctype$X
TEST_PROGRAMS += test-date$X
TEST_PROGRAMS += test-delta$X
+TEST_PROGRAMS += test-dump-cache-tree$X
TEST_PROGRAMS += test-genrandom$X
TEST_PROGRAMS += test-match-trees$X
TEST_PROGRAMS += test-parse-options$X
@@ -1433,14 +1439,14 @@
### Installation rules
-ifeq ($(abspath $(template_dir)),$(template_dir))
+ifneq ($(filter /%,$(firstword $(template_dir))),)
template_instdir = $(template_dir)
else
template_instdir = $(prefix)/$(template_dir)
endif
export template_instdir
-ifeq ($(abspath $(gitexecdir)),$(gitexecdir))
+ifneq ($(filter /%,$(firstword $(gitexecdir))),)
gitexec_instdir = $(gitexecdir)
else
gitexec_instdir = $(prefix)/$(gitexecdir)
diff --git a/builtin-apply.c b/builtin-apply.c
index 1e7f917..f312798 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2441,7 +2441,7 @@
return error("%s: %s", old_name, strerror(errno));
}
- if (!cached)
+ if (!cached && !tpatch)
st_mode = ce_mode_from_stat(*ce, st->st_mode);
if (patch->is_new < 0)
diff --git a/builtin-blame.c b/builtin-blame.c
index aae14ef..9b9f544 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -19,6 +19,7 @@
#include "string-list.h"
#include "mailmap.h"
#include "parse-options.h"
+#include "utf8.h"
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
@@ -1618,13 +1619,14 @@
printf(" %*d", max_orig_digits,
ent->s_lno + 1 + cnt);
- if (!(opt & OUTPUT_NO_AUTHOR))
- printf(" (%-*.*s %10s",
- longest_author, longest_author,
- ci.author,
+ if (!(opt & OUTPUT_NO_AUTHOR)) {
+ int pad = longest_author - utf8_strwidth(ci.author);
+ printf(" (%s%*s %10s",
+ ci.author, pad, "",
format_time(ci.author_time,
ci.author_tz,
show_raw_time));
+ }
printf(" %*d) ",
max_digits, ent->lno + 1 + cnt);
}
@@ -1755,7 +1757,7 @@
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
suspect->commit->object.flags |= METAINFO_SHOWN;
get_commit_info(suspect->commit, &ci, 1);
- num = strlen(ci.author);
+ num = utf8_strwidth(ci.author);
if (longest_author < num)
longest_author = num;
}
diff --git a/builtin-fsck.c b/builtin-fsck.c
index aecc828..64dffa5 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -23,6 +23,7 @@
static int check_strict;
static int keep_cache_objects;
static unsigned char head_sha1[20];
+static const char *head_points_at;
static int errors_found;
static int write_lost_and_found;
static int verbose;
@@ -473,6 +474,8 @@
static void get_default_heads(void)
{
+ if (head_points_at && !is_null_sha1(head_sha1))
+ fsck_handle_ref("HEAD", head_sha1, 0, NULL);
for_each_ref(fsck_handle_ref, NULL);
if (include_reflogs)
for_each_reflog(fsck_handle_reflog, NULL);
@@ -512,14 +515,13 @@
static int fsck_head_link(void)
{
- unsigned char sha1[20];
int flag;
int null_is_error = 0;
- const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
+ head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
@@ -528,7 +530,7 @@
else if (prefixcmp(head_points_at, "refs/heads/"))
return error("HEAD points to something strange (%s)",
head_points_at);
- if (is_null_sha1(sha1)) {
+ if (is_null_sha1(head_sha1)) {
if (null_is_error)
return error("HEAD: detached HEAD points at nothing");
fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
@@ -584,6 +586,7 @@
int cmd_fsck(int argc, const char **argv, const char *prefix)
{
int i, heads;
+ struct alternate_object_database *alt;
errors_found = 0;
@@ -595,17 +598,19 @@
fsck_head_link();
fsck_object_dir(get_object_directory());
+
+ prepare_alt_odb();
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ char namebuf[PATH_MAX];
+ int namelen = alt->name - alt->base;
+ memcpy(namebuf, alt->base, namelen);
+ namebuf[namelen - 1] = 0;
+ fsck_object_dir(namebuf);
+ }
+
if (check_full) {
- struct alternate_object_database *alt;
struct packed_git *p;
- prepare_alt_odb();
- for (alt = alt_odb_list; alt; alt = alt->next) {
- char namebuf[PATH_MAX];
- int namelen = alt->name - alt->base;
- memcpy(namebuf, alt->base, namelen);
- namebuf[namelen - 1] = 0;
- fsck_object_dir(namebuf);
- }
+
prepare_packed_git();
for (p = packed_git; p; p = p->next)
/* verify gives error messages itself */
@@ -624,8 +629,9 @@
heads = 0;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- if (!get_sha1(arg, head_sha1)) {
- struct object *obj = lookup_object(head_sha1);
+ unsigned char sha1[20];
+ if (!get_sha1(arg, sha1)) {
+ struct object *obj = lookup_object(sha1);
/* Error is printed by lookup_object(). */
if (!obj)
diff --git a/builtin-grep.c b/builtin-grep.c
index bebf15c..3f12ba3 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -291,6 +291,8 @@
push_arg("-E");
if (opt->regflags & REG_ICASE)
push_arg("-i");
+ if (opt->binary == GREP_BINARY_NOMATCH)
+ push_arg("-I");
if (opt->word_regexp)
push_arg("-w");
if (opt->name_only)
diff --git a/builtin-merge.c b/builtin-merge.c
index e4555b0..885fad9 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -36,8 +36,8 @@
};
static const char * const builtin_merge_usage[] = {
- "git-merge [options] <remote>...",
- "git-merge [options] <msg> HEAD <remote>",
+ "git merge [options] <remote>...",
+ "git merge [options] <msg> HEAD <remote>",
NULL
};
diff --git a/builtin-mv.c b/builtin-mv.c
index bce9959..01270fe 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -162,7 +162,9 @@
}
argc += last - first;
}
- } else if (lstat(dst, &st) == 0) {
+ } else if (cache_name_pos(src, length) < 0)
+ bad = "not under version control";
+ else if (lstat(dst, &st) == 0) {
bad = "destination exists";
if (force) {
/*
@@ -177,9 +179,7 @@
} else
bad = "Cannot overwrite";
}
- } else if (cache_name_pos(src, length) < 0)
- bad = "not under version control";
- else if (string_list_has_string(&src_for_dst, dst))
+ } else if (string_list_has_string(&src_for_dst, dst))
bad = "multiple sources for the same target";
else
string_list_insert(dst, &src_for_dst);
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 6564a97..6de186c 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -9,9 +9,10 @@
#include "remote.h"
#include "transport.h"
-static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
+static const char receive_pack_usage[] = "git receive-pack <git-dir>";
enum deny_action {
+ DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
DENY_REFUSE,
@@ -19,7 +20,7 @@
static int deny_deletes = 0;
static int deny_non_fast_forwards = 0;
-static enum deny_action deny_current_branch = DENY_WARN;
+static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
static int receive_fsck_objects;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
@@ -214,6 +215,35 @@
return !strcmp(head, ref);
}
+static char *warn_unconfigured_deny_msg[] = {
+ "Updating the currently checked out branch may cause confusion,",
+ "as the index and work tree do not reflect changes that are in HEAD.",
+ "As a result, you may see the changes you just pushed into it",
+ "reverted when you run 'git diff' over there, and you may want",
+ "to run 'git reset --hard' before starting to work to recover.",
+ "",
+ "You can set 'receive.denyCurrentBranch' configuration variable to",
+ "'refuse' in the remote repository to forbid pushing into its",
+ "current branch."
+ "",
+ "To allow pushing into the current branch, you can set it to 'ignore';",
+ "but this is not recommended unless you arranged to update its work",
+ "tree to match what you pushed in some other way.",
+ "",
+ "To squelch this message, you can set it to 'warn'.",
+ "",
+ "Note that the default will change in a future version of git",
+ "to refuse updating the current branch unless you have the",
+ "configuration variable set to either 'ignore' or 'warn'."
+};
+
+static void warn_unconfigured_deny(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
+ warning(warn_unconfigured_deny_msg[i]);
+}
+
static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
@@ -227,22 +257,20 @@
return "funny refname";
}
- switch (deny_current_branch) {
- case DENY_IGNORE:
- break;
- case DENY_WARN:
- if (!is_ref_checked_out(name))
+ if (is_ref_checked_out(name)) {
+ switch (deny_current_branch) {
+ case DENY_IGNORE:
break;
- warning("updating the currently checked out branch; this may"
- " cause confusion,\n"
- "as the index and working tree do not reflect changes"
- " that are now in HEAD.");
- break;
- case DENY_REFUSE:
- if (!is_ref_checked_out(name))
+ case DENY_UNCONFIGURED:
+ case DENY_WARN:
+ warning("updating the current branch");
+ if (deny_current_branch == DENY_UNCONFIGURED)
+ warn_unconfigured_deny();
break;
- error("refusing to update checked out branch: %s", name);
- return "branch is currently checked out";
+ case DENY_REFUSE:
+ error("refusing to update checked out branch: %s", name);
+ return "branch is currently checked out";
+ }
}
if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
diff --git a/builtin-remote.c b/builtin-remote.c
index abc8dd8..db18bcf 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -298,7 +298,7 @@
struct branches_for_remote {
struct remote *remote;
- struct string_list *branches;
+ struct string_list *branches, *skipped;
struct known_remotes *keep;
};
@@ -323,6 +323,16 @@
return 0;
}
+ /* don't delete non-remote refs */
+ if (prefixcmp(refname, "refs/remotes")) {
+ /* advise user how to delete local branches */
+ if (!prefixcmp(refname, "refs/heads/"))
+ string_list_append(abbrev_branch(refname),
+ branches->skipped);
+ /* silently skip over other non-remote refs */
+ return 0;
+ }
+
/* make sure that symrefs are deleted */
if (flags & REF_ISSYMREF)
return unlink(git_path("%s", refname));
@@ -542,8 +552,11 @@
struct strbuf buf = STRBUF_INIT;
struct known_remotes known_remotes = { NULL, NULL };
struct string_list branches = { NULL, 0, 0, 1 };
- struct branches_for_remote cb_data = { NULL, &branches, &known_remotes };
- int i;
+ struct string_list skipped = { NULL, 0, 0, 1 };
+ struct branches_for_remote cb_data = {
+ NULL, &branches, &skipped, &known_remotes
+ };
+ int i, result;
if (argc != 2)
usage_with_options(builtin_remote_usage, options);
@@ -583,14 +596,26 @@
* refs, which are invalidated when deleting a branch.
*/
cb_data.remote = remote;
- i = for_each_ref(add_branch_for_removal, &cb_data);
+ result = for_each_ref(add_branch_for_removal, &cb_data);
strbuf_release(&buf);
- if (!i)
- i = remove_branches(&branches);
+ if (!result)
+ result = remove_branches(&branches);
string_list_clear(&branches, 1);
- return i;
+ if (skipped.nr) {
+ fprintf(stderr, skipped.nr == 1 ?
+ "Note: A non-remote branch was not removed; "
+ "to delete it, use:\n" :
+ "Note: Non-remote branches were not removed; "
+ "to delete them, use:\n");
+ for (i = 0; i < skipped.nr; i++)
+ fprintf(stderr, " git branch -d %s\n",
+ skipped.items[i].string);
+ }
+ string_list_clear(&skipped, 0);
+
+ return result;
}
static void show_list(const char *title, struct string_list *list,
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index bfc78bb..cafc4eb 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -44,6 +44,9 @@
check_symref(argv[0], quiet);
break;
case 2:
+ if (!strcmp(argv[0], "HEAD") &&
+ prefixcmp(argv[1], "refs/heads/"))
+ die("Refusing to point HEAD outside of refs/heads/");
create_symref(argv[0], argv[1], msg);
break;
default:
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
index 25a29f1..0ee0a9a 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -107,7 +107,7 @@
return err;
}
-static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
+static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
diff --git a/cache.h b/cache.h
index 45e713e..2d889de 100644
--- a/cache.h
+++ b/cache.h
@@ -371,6 +371,8 @@
#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);
@@ -542,6 +544,7 @@
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;
diff --git a/command-list.txt b/command-list.txt
index 3583a33..2dc2c33 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -73,6 +73,7 @@
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
diff --git a/commit.c b/commit.c
index aa3b35b..cf72143 100644
--- a/commit.c
+++ b/commit.c
@@ -5,6 +5,7 @@
#include "utf8.h"
#include "diff.h"
#include "revision.h"
+#include "notes.h"
int save_commit_buffer = 1;
diff --git a/config.c b/config.c
index 790405a..e5d5b4b 100644
--- a/config.c
+++ b/config.c
@@ -469,6 +469,11 @@
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/config.mak.in b/config.mak.in
index 55b25c3..7cce0c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -13,9 +13,9 @@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
-gitexecdir = @libexecdir@/git-core/
+gitexecdir = @libexecdir@/git-core
datarootdir = @datarootdir@
-template_dir = @datadir@/git-core/templates/
+template_dir = @datadir@/git-core/templates
mandir=@mandir@
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 81f70ec..307bf5d 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -34,6 +34,12 @@
# 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.
+#
# To submit patches:
#
# *) Read Documentation/SubmittingPatches
@@ -116,10 +122,26 @@
fi
fi
+ local w
+ local i
+
+ 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="*"
+ if git rev-parse --quiet --verify HEAD >/dev/null; then
+ git diff-index --cached --quiet \
+ --ignore-submodules HEAD -- || i="+"
+ else
+ i="#"
+ fi
+ fi
+ fi
+
if [ -n "${1-}" ]; then
- printf "$1" "${b##refs/heads/}$r"
+ printf "$1" "${b##refs/heads/}$w$i$r"
else
- printf " (%s)" "${b##refs/heads/}$r"
+ printf " (%s)" "${b##refs/heads/}$w$i$r"
fi
fi
}
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index a48540a..24d9312 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -2,7 +2,7 @@
EMACS = emacs
-ELC = git.elc vc-git.elc git-blame.elc
+ELC = git.elc git-blame.elc
INSTALL ?= install
INSTALL_ELC = $(INSTALL) -m 644
prefix ?= $(HOME)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 09e8bae..fcbe2d9 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -1,6 +1,6 @@
;;; git.el --- A user interface for git
-;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard <julliard@winehq.org>
;; Version: 1.0
@@ -34,15 +34,21 @@
;; To start: `M-x git-status'
;;
;; TODO
-;; - portability to XEmacs
;; - diff against other branch
;; - renaming files from the status buffer
;; - creating tags
;; - fetch/pull
-;; - switching branches
;; - revlist browser
;; - git-show-branch browser
-;; - menus
+;;
+
+;;; Compatibility:
+;;
+;; This file works on GNU Emacs 21 or later. It may work on older
+;; versions but this is not guaranteed.
+;;
+;; It may work on XEmacs 21, provided that you first install the ewoc
+;; and log-edit packages.
;;
(eval-when-compile (require 'cl))
@@ -222,7 +228,7 @@
(with-current-buffer buffer
(cd dir)
(apply #'call-process-region start end program
- nil (list output-buffer nil) nil args))))
+ nil (list output-buffer t) nil args))))
(defun git-run-command-buffer (buffer-name &rest args)
"Run a git command, sending the output to a buffer named BUFFER-NAME."
@@ -239,13 +245,15 @@
(defun git-run-command-region (buffer start end env &rest args)
"Run a git command with specified buffer region as input."
- (unless (eq 0 (if env
- (git-run-process-region
- buffer start end "env"
- (append (git-get-env-strings env) (list "git") args))
+ (with-temp-buffer
+ (if (eq 0 (if env
(git-run-process-region
- buffer start end "git" args)))
- (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
+ buffer start end "env"
+ (append (git-get-env-strings env) (list "git") args))
+ (git-run-process-region buffer start end "git" args)))
+ (buffer-string)
+ (display-message-or-buffer (current-buffer))
+ nil)))
(defun git-run-hook (hook env &rest args)
"Run a git hook and display its output if any."
@@ -397,6 +405,17 @@
(unless newval (push "-d" args))
(apply 'git-call-process-display-error "update-ref" args)))
+(defun git-for-each-ref (&rest specs)
+ "Return a list of refs using git-for-each-ref.
+Each entry is a cons of (SHORT-NAME . FULL-NAME)."
+ (let (refs)
+ (with-temp-buffer
+ (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs)
+ (goto-char (point-min))
+ (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t)
+ (push (cons (match-string 1) (match-string 0)) refs)))
+ (nreverse refs)))
+
(defun git-read-tree (tree &optional index-file)
"Read a tree into the index file."
(let ((process-environment
@@ -447,18 +466,16 @@
(setq coding-system-for-write buffer-file-coding-system))
(let ((commit
(git-get-string-sha1
- (with-output-to-string
- (with-current-buffer standard-output
- (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
- ("GIT_AUTHOR_EMAIL" . ,author-email)
- ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
- ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
- (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
- (apply #'git-run-command-region
- buffer log-start log-end env
- "commit-tree" tree (nreverse args))))))))
- (and (git-update-ref "HEAD" commit head subject)
- commit))))
+ (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
+ ("GIT_AUTHOR_EMAIL" . ,author-email)
+ ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
+ ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
+ (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
+ (apply #'git-run-command-region
+ buffer log-start log-end env
+ "commit-tree" tree (nreverse args))))))
+ (when commit (git-update-ref "HEAD" commit head subject))
+ commit)))
(defun git-empty-db-p ()
"Check if the git db is empty (no commit done yet)."
@@ -562,29 +579,29 @@
(let* ((old-type (lsh (or old-perm 0) -9))
(new-type (lsh (or new-perm 0) -9))
(str (case new-type
- (?\100 ;; file
+ (64 ;; file
(case old-type
- (?\100 nil)
- (?\120 " (type change symlink -> file)")
- (?\160 " (type change subproject -> file)")))
- (?\120 ;; symlink
+ (64 nil)
+ (80 " (type change symlink -> file)")
+ (112 " (type change subproject -> file)")))
+ (80 ;; symlink
(case old-type
- (?\100 " (type change file -> symlink)")
- (?\160 " (type change subproject -> symlink)")
+ (64 " (type change file -> symlink)")
+ (112 " (type change subproject -> symlink)")
(t " (symlink)")))
- (?\160 ;; subproject
+ (112 ;; subproject
(case old-type
- (?\100 " (type change file -> subproject)")
- (?\120 " (type change symlink -> subproject)")
+ (64 " (type change file -> subproject)")
+ (80 " (type change symlink -> subproject)")
(t " (subproject)")))
- (?\110 nil) ;; directory (internal, not a real git state)
- (?\000 ;; deleted or unknown
+ (72 nil) ;; directory (internal, not a real git state)
+ (0 ;; deleted or unknown
(case old-type
- (?\120 " (symlink)")
- (?\160 " (subproject)")))
+ (80 " (symlink)")
+ (112 " (subproject)")))
(t (format " (unknown type %o)" new-type)))))
(cond (str (propertize str 'face 'git-status-face))
- ((eq new-type ?\110) "/")
+ ((eq new-type 72) "/")
(t ""))))
(defun git-rename-as-string (info)
@@ -1320,6 +1337,7 @@
(log-edit-diff-function . git-log-edit-diff)) buffer)
(log-edit 'git-do-commit nil 'git-log-edit-files buffer))
(setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+ (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ ]*$"))
(setq buffer-file-coding-system coding-system)
(re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
@@ -1356,6 +1374,36 @@
(push (match-string 1) files)))
files))
+(defun git-read-commit-name (prompt &optional default)
+ "Ask for a commit name, with completion for local branch, remote branch and tag."
+ (completing-read prompt
+ (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref)))
+ nil nil nil nil default))
+
+(defun git-checkout (branch &optional merge)
+ "Checkout a branch, tag, or any commit.
+Use a prefix arg if git should merge while checking out."
+ (interactive
+ (list (git-read-commit-name "Checkout: ")
+ current-prefix-arg))
+ (unless git-status (error "Not in git-status buffer."))
+ (let ((args (list branch "--")))
+ (when merge (push "-m" args))
+ (when (apply #'git-call-process-display-error "checkout" args)
+ (git-update-status-files))))
+
+(defun git-branch (branch)
+ "Create a branch from the current HEAD and switch to it."
+ (interactive (list (git-read-commit-name "Branch: ")))
+ (unless git-status (error "Not in git-status buffer."))
+ (if (git-rev-parse (concat "refs/heads/" branch))
+ (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch))
+ (and (git-call-process-display-error "branch" "-f" branch)
+ (git-call-process-display-error "checkout" branch))
+ (message "Canceled."))
+ (git-call-process-display-error "checkout" "-b" branch))
+ (git-refresh-ewoc-hf git-status))
+
(defun git-amend-commit ()
"Undo the last commit on HEAD, and set things up to commit an
amended version of it."
@@ -1372,6 +1420,44 @@
(git-setup-commit-buffer commit)
(git-commit-file))))
+(defun git-cherry-pick-commit (arg)
+ "Cherry-pick a commit."
+ (interactive (list (git-read-commit-name "Cherry-pick commit: ")))
+ (unless git-status (error "Not in git-status buffer."))
+ (let ((commit (git-rev-parse (concat arg "^0"))))
+ (unless commit (error "Not a valid commit '%s'." arg))
+ (when (git-rev-parse (concat commit "^2"))
+ (error "Cannot cherry-pick a merge commit."))
+ (let ((files (git-get-commit-files commit))
+ (ok (git-call-process-display-error "cherry-pick" "-n" commit)))
+ (git-update-status-files files ok)
+ (with-current-buffer (git-setup-commit-buffer commit)
+ (goto-char (point-min))
+ (if (re-search-forward "^\n*Signed-off-by:" nil t 1)
+ (goto-char (match-beginning 0))
+ (goto-char (point-max)))
+ (insert "(cherry picked from commit " commit ")\n"))
+ (when ok (git-commit-file)))))
+
+(defun git-revert-commit (arg)
+ "Revert a commit."
+ (interactive (list (git-read-commit-name "Revert commit: ")))
+ (unless git-status (error "Not in git-status buffer."))
+ (let ((commit (git-rev-parse (concat arg "^0"))))
+ (unless commit (error "Not a valid commit '%s'." arg))
+ (when (git-rev-parse (concat commit "^2"))
+ (error "Cannot revert a merge commit."))
+ (let ((files (git-get-commit-files commit))
+ (subject (git-get-commit-description commit))
+ (ok (git-call-process-display-error "revert" "-n" commit)))
+ (git-update-status-files files ok)
+ (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject)
+ (setq subject (match-string 1 subject)))
+ (git-setup-log-buffer (get-buffer-create "*git-commit*")
+ (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil
+ (format "This reverts commit %s.\n" commit))
+ (when ok (git-commit-file)))))
+
(defun git-find-file ()
"Visit the current file in its own buffer."
(interactive)
@@ -1471,6 +1557,10 @@
(define-key map "\M-\C-?" 'git-unmark-all)
; the commit submap
(define-key commit-map "\C-a" 'git-amend-commit)
+ (define-key commit-map "\C-b" 'git-branch)
+ (define-key commit-map "\C-o" 'git-checkout)
+ (define-key commit-map "\C-p" 'git-cherry-pick-commit)
+ (define-key commit-map "\C-v" 'git-revert-commit)
; the diff submap
(define-key diff-map "b" 'git-diff-file-base)
(define-key diff-map "c" 'git-diff-file-combined)
@@ -1491,6 +1581,10 @@
`("Git"
["Refresh" git-refresh-status t]
["Commit" git-commit-file t]
+ ["Checkout..." git-checkout t]
+ ["New Branch..." git-branch t]
+ ["Cherry-pick Commit..." git-cherry-pick-commit t]
+ ["Revert Commit..." git-revert-commit t]
("Merge"
["Next Unmerged File" git-next-unmerged-file t]
["Prev Unmerged File" git-prev-unmerged-file t]
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
deleted file mode 100644
index b8f6be5..0000000
--- a/contrib/emacs/vc-git.el
+++ /dev/null
@@ -1,216 +0,0 @@
-;;; vc-git.el --- VC backend for the git version control system
-
-;; Copyright (C) 2006 Alexandre Julliard
-
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be
-;; useful, but WITHOUT ANY WARRANTY; without even the implied
-;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-;; PURPOSE. See the GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public
-;; License along with this program; if not, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
-
-;;; Commentary:
-
-;; This file contains a VC backend for the git version control
-;; system.
-;;
-;; To install: put this file on the load-path and add GIT to the list
-;; of supported backends in `vc-handled-backends'; the following line,
-;; placed in your ~/.emacs, will accomplish this:
-;;
-;; (add-to-list 'vc-handled-backends 'GIT)
-;;
-;; TODO
-;; - changelog generation
-;; - working with revisions other than HEAD
-;;
-
-(eval-when-compile (require 'cl))
-
-(defvar git-commits-coding-system 'utf-8
- "Default coding system for git commits.")
-
-(defun vc-git--run-command-string (file &rest args)
- "Run a git command on FILE and return its output as string."
- (let* ((ok t)
- (str (with-output-to-string
- (with-current-buffer standard-output
- (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil
- (append args (list (file-relative-name file)))))
- (setq ok nil))))))
- (and ok str)))
-
-(defun vc-git--run-command (file &rest args)
- "Run a git command on FILE, discarding any output."
- (let ((name (file-relative-name file)))
- (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name))))))
-
-(defun vc-git-registered (file)
- "Check whether FILE is registered with git."
- (with-temp-buffer
- (let* ((dir (file-name-directory file))
- (name (file-relative-name file dir)))
- (and (ignore-errors
- (when dir (cd dir))
- (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
- (let ((str (buffer-string)))
- (and (> (length str) (length name))
- (string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
-
-(defun vc-git-state (file)
- "git-specific version of `vc-state'."
- (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--")))
- (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff))
- 'edited
- 'up-to-date)))
-
-(defun vc-git-workfile-version (file)
- "git-specific version of `vc-workfile-version'."
- (let ((str (with-output-to-string
- (with-current-buffer standard-output
- (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD")))))
- (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
- (match-string 2 str)
- str)))
-
-(defun vc-git-symbolic-commit (commit)
- "Translate COMMIT string into symbolic form.
-Returns nil if not possible."
- (and commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "name-rev"
- "--name-only" "--tags"
- commit))
- (goto-char (point-min))
- (= (forward-line 2) 1)
- (bolp)
- (buffer-substring-no-properties (point-min) (1- (point-max)))))))
-
-(defun vc-git-previous-version (file rev)
- "git-specific version of `vc-previous-version'."
- (let ((default-directory (file-name-directory (expand-file-name file)))
- (file (file-name-nondirectory file)))
- (vc-git-symbolic-commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "-2" rev "--" file))
- (goto-char (point-max))
- (bolp)
- (zerop (forward-line -1))
- (not (bobp))
- (buffer-substring-no-properties
- (point)
- (1- (point-max))))))))
-
-(defun vc-git-next-version (file rev)
- "git-specific version of `vc-next-version'."
- (let* ((default-directory (file-name-directory
- (expand-file-name file)))
- (file (file-name-nondirectory file))
- (current-rev
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "-1" rev "--" file))
- (goto-char (point-max))
- (bolp)
- (zerop (forward-line -1))
- (bobp)
- (buffer-substring-no-properties
- (point)
- (1- (point-max)))))))
- (and current-rev
- (vc-git-symbolic-commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "HEAD" "--" file))
- (goto-char (point-min))
- (search-forward current-rev nil t)
- (zerop (forward-line -1))
- (buffer-substring-no-properties
- (point)
- (progn (forward-line 1) (1- (point))))))))))
-
-(defun vc-git-revert (file &optional contents-done)
- "Revert FILE to the version stored in the git repository."
- (if contents-done
- (vc-git--run-command file "update-index" "--")
- (vc-git--run-command file "checkout" "HEAD")))
-
-(defun vc-git-checkout-model (file)
- 'implicit)
-
-(defun vc-git-workfile-unchanged-p (file)
- (let ((sha1 (vc-git--run-command-string file "hash-object" "--"))
- (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--")))
- (and head
- (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head)
- (string= (car (split-string sha1 "\n")) (match-string 1 head)))))
-
-(defun vc-git-register (file &optional rev comment)
- "Register FILE into the git version-control system."
- (vc-git--run-command file "update-index" "--add" "--"))
-
-(defun vc-git-print-log (file &optional buffer)
- (let ((name (file-relative-name file))
- (coding-system-for-read git-commits-coding-system))
- (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
-
-(defun vc-git-diff (file &optional rev1 rev2 buffer)
- (let ((name (file-relative-name file))
- (buf (or buffer "*vc-diff*")))
- (if (and rev1 rev2)
- (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
- (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
- ; git-diff-index doesn't set exit status like diff does
- (if (vc-git-workfile-unchanged-p file) 0 1)))
-
-(defun vc-git-checkin (file rev comment)
- (let ((coding-system-for-write git-commits-coding-system))
- (vc-git--run-command file "commit" "-m" comment "--only" "--")))
-
-(defun vc-git-checkout (file &optional editable rev destfile)
- (if destfile
- (let ((fullname (substring
- (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--")
- 0 -1))
- (coding-system-for-read 'no-conversion)
- (coding-system-for-write 'no-conversion))
- (with-temp-file destfile
- (eq 0 (call-process "git" nil t nil "cat-file" "blob"
- (concat (or rev "HEAD") ":" fullname)))))
- (vc-git--run-command file "checkout" (or rev "HEAD"))))
-
-(defun vc-git-annotate-command (file buf &optional rev)
- ; FIXME: rev is ignored
- (let ((name (file-relative-name file)))
- (call-process "git" nil buf nil "blame" name)))
-
-(defun vc-git-annotate-time ()
- (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
- (vc-annotate-convert-time
- (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
-
-;; Not really useful since we can't do anything with the revision yet
-;;(defun vc-annotate-extract-revision-at-line ()
-;; (save-excursion
-;; (move-beginning-of-line 1)
-;; (and (looking-at "[0-9a-f]+")
-;; (buffer-substring (match-beginning 0) (match-end 0)))))
-
-(provide 'vc-git)
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
new file mode 100755
index 0000000..c364dda
--- /dev/null
+++ b/contrib/git-resurrect.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
+LONG_USAGE="git-resurrect attempts to find traces of a branch tip
+called <name>, and tries to resurrect it. Currently, the reflog is
+searched for checkout messages, and with -r also merge messages. With
+-m and -t, the history of all refs is scanned for Merge <name> into
+other/Merge <other> into <name> (respectively) commit subjects, which
+is rather slow but allows you to resurrect other people's topic
+branches."
+
+OPTIONS_SPEC="\
+git resurrect $USAGE
+--
+b,branch= save branch as <newname> instead of <name>
+a,all same as -l -r -m -t
+k,keep-going full rev-list scan (instead of first match)
+l,reflog scan reflog for checkouts (enabled by default)
+r,reflog-merges scan for merges recorded in reflog
+m,merges scan for merges into other branches (slow)
+t,merge-targets scan for merges of other branches into <name>
+n,dry-run don't recreate the branch"
+
+. git-sh-setup
+
+search_reflog () {
+ sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
+ < "$GIT_DIR"/logs/HEAD
+}
+
+search_reflog_merges () {
+ git rev-parse $(
+ sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
+ < "$GIT_DIR"/logs/HEAD
+ )
+}
+
+_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+
+search_merges () {
+ git rev-list --all --grep="Merge branch '$1'" \
+ --pretty=tformat:"%P %s" |
+ sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
+}
+
+search_merge_targets () {
+ git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
+ --pretty=tformat:"%H %s" --all |
+ sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
+}
+
+dry_run=
+early_exit=q
+scan_reflog=t
+scan_reflog_merges=
+scan_merges=
+scan_merge_targets=
+new_name=
+
+while test "$#" != 0; do
+ case "$1" in
+ -b|--branch)
+ shift
+ new_name="$1"
+ ;;
+ -n|--dry-run)
+ dry_run=t
+ ;;
+ --no-dry-run)
+ dry_run=
+ ;;
+ -k|--keep-going)
+ early_exit=
+ ;;
+ --no-keep-going)
+ early_exit=q
+ ;;
+ -m|--merges)
+ scan_merges=t
+ ;;
+ --no-merges)
+ scan_merges=
+ ;;
+ -l|--reflog)
+ scan_reflog=t
+ ;;
+ --no-reflog)
+ scan_reflog=
+ ;;
+ -r|--reflog_merges)
+ scan_reflog_merges=t
+ ;;
+ --no-reflog_merges)
+ scan_reflog_merges=
+ ;;
+ -t|--merge-targets)
+ scan_merge_targets=t
+ ;;
+ --no-merge-targets)
+ scan_merge_targets=
+ ;;
+ -a|--all)
+ scan_reflog=t
+ scan_reflog_merges=t
+ scan_merges=t
+ scan_merge_targets=t
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ shift
+done
+
+test "$#" = 1 || usage
+
+all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
+if test -z "$all_strategies"; then
+ die "must enable at least one of -lrmt"
+fi
+
+branch="$1"
+test -z "$new_name" && new_name="$branch"
+
+if test ! -z "$scan_reflog"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$(search_reflog $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_reflog_merges"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$candidates $(search_reflog_merges $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_merges"; then
+ candidates="$candidates $(search_merges $branch)"
+fi
+if test ! -z "$scan_merge_targets"; then
+ candidates="$candidates $(search_merge_targets $branch)"
+fi
+
+candidates="$(git rev-parse $candidates | sort -u)"
+
+if test -z "$candidates"; then
+ hint=
+ test "z$all_strategies" != "ztttt" \
+ && hint=" (maybe try again with -a)"
+ die "no candidates for $branch found$hint"
+fi
+
+echo "** Candidates for $branch **"
+for cmt in $candidates; do
+ git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
+done \
+| sort -n | cut -d: -f2-
+
+newest="$(git rev-list -1 $candidates)"
+if test ! -z "$dry_run"; then
+ printf "** Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
+ printf "** Restoring $new_name to "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ git branch $new_name $newest
+else
+ printf "Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ echo "** $new_name already exists, doing nothing"
+fi
diff --git a/environment.c b/environment.c
index e278bce..0edae21 100644
--- a/environment.c
+++ b/environment.c
@@ -45,6 +45,7 @@
/* 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/git-am.sh b/git-am.sh
index b598b43..8bcb206 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -23,6 +23,8 @@
r,resolved to be used after a patch failure
skip skip the current patch
abort restore the original branch and abort the patching operation.
+committer-date-is-author-date lie about committer date
+ignore-date use current timestamp for author date
rebasing* (internal use for git-rebase)"
. git-sh-setup
@@ -133,6 +135,8 @@
sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
resolvemsg= resume=
git_apply_opt=
+committer_date_is_author_date=
+ignore_date=
while test $# != 0
do
@@ -170,6 +174,10 @@
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
--reject)
git_apply_opt="$git_apply_opt $1" ;;
+ --committer-date-is-author-date)
+ committer_date_is_author_date=t ;;
+ --ignore-date)
+ ignore_date=t ;;
--)
shift; break ;;
*)
@@ -520,7 +528,18 @@
tree=$(git write-tree) &&
parent=$(git rev-parse --verify HEAD) &&
- commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
+ commit=$(
+ if test -n "$ignore_date"
+ then
+ GIT_AUTHOR_DATE=
+ fi
+ if test -n "$committer_date_is_author_date"
+ then
+ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+ export GIT_COMMITTER_DATE
+ fi &&
+ git commit-tree $tree -p $parent <"$dotest/final-commit"
+ ) &&
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
stop_here $this
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index eb62f71..86eef56 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -108,7 +108,7 @@
. git-sh-setup
if [ "$(is_bare_repository)" = false ]; then
- git diff-files --quiet &&
+ git diff-files --ignore-submodules --quiet &&
git diff-index --cached --quiet HEAD -- ||
die "Cannot rewrite branch(es) with a dirty working directory."
fi
@@ -469,19 +469,20 @@
trap - 0
+unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+test -z "$ORIG_GIT_DIR" || {
+ GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+}
+test -z "$ORIG_GIT_WORK_TREE" || {
+ GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+ export GIT_WORK_TREE
+}
+test -z "$ORIG_GIT_INDEX_FILE" || {
+ GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+ export GIT_INDEX_FILE
+}
+
if [ "$(is_bare_repository)" = false ]; then
- unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
- test -z "$ORIG_GIT_DIR" || {
- GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
- }
- test -z "$ORIG_GIT_WORK_TREE" || {
- GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
- export GIT_WORK_TREE
- }
- test -z "$ORIG_GIT_INDEX_FILE" || {
- GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
- export GIT_INDEX_FILE
- }
git read-tree -u -m HEAD
fi
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 09f3a10..87fa88a 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -13,7 +13,6 @@
OPTIONS_SPEC=
. git-sh-setup
require_work_tree
-prefix=$(git rev-parse --show-prefix)
# Returns true if the mode reflects a symlink
is_symlink () {
@@ -127,6 +126,14 @@
fi
}
+checkout_staged_file () {
+ tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ')
+
+ if test $? -eq 0 -a -n "$tmpfile" ; then
+ mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
+ fi
+}
+
merge_file () {
MERGED="$1"
@@ -153,9 +160,9 @@
local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
- base_present && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
- local_present && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
- remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
+ base_present && checkout_staged_file 1 "$MERGED" "$BASE"
+ local_present && checkout_staged_file 2 "$MERGED" "$LOCAL"
+ remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
if test -z "$local_mode" -o -z "$remote_mode"; then
echo "Deleted merge conflict for '$MERGED':"
diff --git a/git-notes.sh b/git-notes.sh
new file mode 100755
index 0000000..bfdbaa8
--- /dev/null
+++ b/git-notes.sh
@@ -0,0 +1,65 @@
+#!/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.c b/git.c
index 320cb43..c2b181e 100644
--- a/git.c
+++ b/git.c
@@ -2,6 +2,7 @@
#include "exec_cmd.h"
#include "cache.h"
#include "quote.h"
+#include "run-command.h"
const char git_usage_string[] =
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
@@ -219,7 +220,7 @@
int option;
};
-static int run_command(struct cmd_struct *p, int argc, const char **argv)
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
{
int status;
struct stat st;
@@ -384,7 +385,7 @@
struct cmd_struct *p = commands+i;
if (strcmp(p->cmd, cmd))
continue;
- exit(run_command(p, argc, argv));
+ exit(run_builtin(p, argc, argv));
}
}
@@ -392,6 +393,7 @@
{
struct strbuf cmd = STRBUF_INIT;
const char *tmp;
+ int status;
strbuf_addf(&cmd, "git-%s", argv[0]);
@@ -406,10 +408,17 @@
trace_argv_printf(argv, "trace: exec:");
- /* execvp() can only ever return if it fails */
- execvp(cmd.buf, (char **)argv);
-
- trace_printf("trace: exec failed: %s\n", strerror(errno));
+ /*
+ * if we fail because the command is not found, it is
+ * OK to return. Otherwise, we just pass along the status code.
+ */
+ status = run_command_v_opt(argv, 0);
+ if (status != -ERR_RUN_COMMAND_EXEC) {
+ if (IS_RUN_COMMAND_ERR(status))
+ die("unable to run '%s'", argv[0]);
+ exit(-status);
+ }
+ errno = ENOENT; /* as if we called execvp */
argv[0] = tmp;
diff --git a/git.spec.in b/git.spec.in
index 069ace0..4be0834 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -97,7 +97,7 @@
%description -n perl-Git
Perl interface to Git
-%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-core-%{version}
+%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-%{version}
%prep
%setup -q
@@ -190,6 +190,9 @@
# No files for you!
%changelog
+* Mon Feb 04 2009 David J. Mellor <dmellor@whistlingcat.com>
+- fixed broken git help -w after renaming the git-core package to git.
+
* Fri Sep 12 2008 Quy Tonthat <qtonthat@gmail.com>
- move git-cvsserver to bindir.
diff --git a/http-push.c b/http-push.c
index 18d81ac..a8ae545 100644
--- a/http-push.c
+++ b/http-push.c
@@ -210,6 +210,15 @@
return dav_headers;
}
+static void append_remote_object_url(struct strbuf *buf, const char *url,
+ const char *hex,
+ int only_two_digit_prefix)
+{
+ strbuf_addf(buf, "%sobjects/%.*s/", url, 2, hex);
+ if (!only_two_digit_prefix)
+ strbuf_addf(buf, "%s", hex+2);
+}
+
static void finish_request(struct transfer_request *request);
static void release_request(struct transfer_request *request);
@@ -222,6 +231,15 @@
}
#ifdef USE_CURL_MULTI
+
+static char *get_remote_object_url(const char *url, const char *hex,
+ int only_two_digit_prefix)
+{
+ struct strbuf buf = STRBUF_INIT;
+ append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
+ return strbuf_detach(&buf, NULL);
+}
+
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
void *data)
{
@@ -256,7 +274,6 @@
char *filename;
char prevfile[PATH_MAX];
char *url;
- char *posn;
int prevlocal;
unsigned char prev_buf[PREV_BUF_SIZE];
ssize_t prev_read = 0;
@@ -306,17 +323,8 @@
git_SHA1_Init(&request->c);
- url = xmalloc(strlen(remote->url) + 50);
- request->url = xmalloc(strlen(remote->url) + 50);
- strcpy(url, remote->url);
- posn = url + strlen(remote->url);
- strcpy(posn, "objects/");
- posn += 8;
- memcpy(posn, hex, 2);
- posn += 2;
- *(posn++) = '/';
- strcpy(posn, hex + 2);
- strcpy(request->url, url);
+ url = get_remote_object_url(remote->url, hex, 0);
+ request->url = xstrdup(url);
/* If a previous temp file is present, process what was already
fetched. */
@@ -389,16 +397,8 @@
{
char *hex = sha1_to_hex(request->obj->sha1);
struct active_request_slot *slot;
- char *posn;
- request->url = xmalloc(strlen(remote->url) + 13);
- strcpy(request->url, remote->url);
- posn = request->url + strlen(remote->url);
- strcpy(posn, "objects/");
- posn += 8;
- memcpy(posn, hex, 2);
- posn += 2;
- strcpy(posn, "/");
+ request->url = get_remote_object_url(remote->url, hex, 1);
slot = get_active_slot();
slot->callback_func = process_response;
@@ -513,7 +513,7 @@
{
char *hex = sha1_to_hex(request->obj->sha1);
struct active_request_slot *slot;
- char *posn;
+ struct strbuf buf = STRBUF_INIT;
enum object_type type;
char hdr[50];
void *unpacked;
@@ -552,21 +552,14 @@
request->buffer.buf.len = stream.total_out;
- request->url = xmalloc(strlen(remote->url) +
- strlen(request->lock->token) + 51);
- strcpy(request->url, remote->url);
- posn = request->url + strlen(remote->url);
- strcpy(posn, "objects/");
- posn += 8;
- memcpy(posn, hex, 2);
- posn += 2;
- *(posn++) = '/';
- strcpy(posn, hex + 2);
- request->dest = xmalloc(strlen(request->url) + 14);
- sprintf(request->dest, "Destination: %s", request->url);
- posn += 38;
- *(posn++) = '_';
- strcpy(posn, request->lock->token);
+ strbuf_addstr(&buf, "Destination: ");
+ append_remote_object_url(&buf, remote->url, hex, 0);
+ request->dest = strbuf_detach(&buf, NULL);
+
+ append_remote_object_url(&buf, remote->url, hex, 0);
+ strbuf_addstr(&buf, "_");
+ strbuf_addstr(&buf, request->lock->token);
+ request->url = strbuf_detach(&buf, NULL);
slot = get_active_slot();
slot->callback_func = process_response;
diff --git a/merge-index.c b/merge-index.c
index c00a2b3..aa9cf23 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -92,7 +92,7 @@
signal(SIGCHLD, SIG_DFL);
if (argc < 3)
- usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
+ usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
git_extract_argv0_path(argv[0]);
diff --git a/merge-tree.c b/merge-tree.c
index f18201a..f01e7c8 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -4,7 +4,7 @@
#include "blob.h"
#include "exec_cmd.h"
-static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
+static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
static int resolve_directories = 1;
struct merge_list {
diff --git a/mktag.c b/mktag.c
index 6d5083e..99a356e 100644
--- a/mktag.c
+++ b/mktag.c
@@ -158,7 +158,7 @@
unsigned char result_sha1[20];
if (argc != 1)
- usage("git-mktag < signaturefile");
+ usage("git mktag < signaturefile");
git_extract_argv0_path(argv[0]);
diff --git a/mktree.c b/mktree.c
index 6283bc3..137a095 100644
--- a/mktree.c
+++ b/mktree.c
@@ -62,7 +62,7 @@
write_sha1_file(buf.buf, buf.len, tree_type, sha1);
}
-static const char mktree_usage[] = "git-mktree [-z]";
+static const char mktree_usage[] = "git mktree [-z]";
int main(int ac, char **av)
{
diff --git a/notes.c b/notes.c
new file mode 100644
index 0000000..bd73784
--- /dev/null
+++ b/notes.c
@@ -0,0 +1,160 @@
+#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
new file mode 100644
index 0000000..79d21b6
--- /dev/null
+++ b/notes.h
@@ -0,0 +1,7 @@
+#ifndef NOTES_H
+#define NOTES_H
+
+void get_commit_notes(const struct commit *commit, struct strbuf *sb,
+ const char *output_encoding);
+
+#endif
diff --git a/patch-id.c b/patch-id.c
index 3660ad4..0df4cb0 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -73,7 +73,7 @@
flush_current_id(patchlen, sha1, &ctx);
}
-static const char patch_id_usage[] = "git-patch-id < patch";
+static const char patch_id_usage[] = "git patch-id < patch";
int main(int argc, char **argv)
{
diff --git a/path.c b/path.c
index a074aea..108d9e9 100644
--- a/path.c
+++ b/path.c
@@ -154,7 +154,7 @@
/* Make sure it is a "refs/.." symlink */
if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5))
+ if (len >= 11 && !memcmp("refs/heads/", buffer, 11))
return 0;
return -1;
}
@@ -178,7 +178,7 @@
len -= 4;
while (len && isspace(*buf))
buf++, len--;
- if (len >= 5 && !memcmp("refs/", buf, 5))
+ if (len >= 11 && !memcmp("refs/heads/", buf, 11))
return 0;
}
diff --git a/pretty.c b/pretty.c
index cc460b5..8d4dbc9 100644
--- a/pretty.c
+++ b/pretty.c
@@ -6,6 +6,7 @@
#include "string-list.h"
#include "mailmap.h"
#include "log-tree.h"
+#include "notes.h"
#include "color.h"
static char *user_format;
@@ -920,5 +921,9 @@
*/
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);
}
diff --git a/read-cache.c b/read-cache.c
index b1475ff..940ec76 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1574,6 +1574,26 @@
default:
die("unexpected diff status %c", p->status);
case DIFF_STATUS_UNMERGED:
+ /*
+ * ADD_CACHE_IGNORE_REMOVAL is unset if "git
+ * add -u" is calling us, In such a case, a
+ * missing work tree file needs to be removed
+ * if there is an unmerged entry at stage #2,
+ * but such a diff record is followed by
+ * another with DIFF_STATUS_DELETED (and if
+ * there is no stage #2, we won't see DELETED
+ * nor MODIFIED). We can simply continue
+ * either way.
+ */
+ if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
+ continue;
+ /*
+ * Otherwise, it is "git add path" is asking
+ * to explicitly add it; we fall through. A
+ * missing work tree file is an error and is
+ * caught by add_file_to_index() in such a
+ * case.
+ */
case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED:
if (add_file_to_index(&the_index, path, data->flags)) {
diff --git a/run-command.c b/run-command.c
index db9ce59..b05c734 100644
--- a/run-command.c
+++ b/run-command.c
@@ -118,7 +118,9 @@
} else {
execvp(cmd->argv[0], (char *const*) cmd->argv);
}
- die("exec %s failed.", cmd->argv[0]);
+ trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
+ strerror(errno));
+ exit(127);
}
#else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
@@ -187,6 +189,7 @@
#endif
if (cmd->pid < 0) {
+ int err = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
@@ -197,7 +200,9 @@
close(cmd->out);
if (need_err)
close_pair(fderr);
- return -ERR_RUN_COMMAND_FORK;
+ return err == ENOENT ?
+ -ERR_RUN_COMMAND_EXEC :
+ -ERR_RUN_COMMAND_FORK;
}
if (need_in)
@@ -236,9 +241,14 @@
if (!WIFEXITED(status))
return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
code = WEXITSTATUS(status);
- if (code)
+ switch (code) {
+ case 127:
+ return -ERR_RUN_COMMAND_EXEC;
+ case 0:
+ return 0;
+ default:
return -code;
- return 0;
+ }
}
}
diff --git a/run-command.h b/run-command.h
index 0211e1d..15e870a 100644
--- a/run-command.h
+++ b/run-command.h
@@ -10,6 +10,7 @@
ERR_RUN_COMMAND_WAITPID_SIGNAL,
ERR_RUN_COMMAND_WAITPID_NOEXIT,
};
+#define IS_RUN_COMMAND_ERR(x) ((x) <= -ERR_RUN_COMMAND_FORK)
struct child_process {
const char **argv;
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
new file mode 100755
index 0000000..569f341
--- /dev/null
+++ b/t/t1401-symbolic-ref.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='basic symbolic-ref tests'
+. ./test-lib.sh
+
+# If the tests munging HEAD fail, they can break detection of
+# the git repo, meaning that further tests will operate on
+# the surrounding git repo instead of the trash directory.
+reset_to_sane() {
+ echo ref: refs/heads/foo >.git/HEAD
+}
+
+test_expect_success 'symbolic-ref writes HEAD' '
+ git symbolic-ref HEAD refs/heads/foo &&
+ echo ref: refs/heads/foo >expect &&
+ test_cmp expect .git/HEAD
+'
+
+test_expect_success 'symbolic-ref reads HEAD' '
+ echo refs/heads/foo >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
+ test_must_fail git symbolic-ref HEAD foo
+'
+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`
+'
+reset_to_sane
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
new file mode 100755
index 0000000..4597af0
--- /dev/null
+++ b/t/t1450-fsck.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='git fsck random collection of tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A fileA one &&
+ git checkout HEAD^0 &&
+ test_commit B fileB two &&
+ git tag -d A B &&
+ git reflog expire --expire=now --all
+'
+
+test_expect_success 'HEAD is part of refs' '
+ test 0 = $(git fsck | wc -l)
+'
+
+test_expect_success 'loose objects borrowed from alternate are not missing' '
+ mkdir another &&
+ (
+ cd another &&
+ git init &&
+ echo ../../../.git/objects >.git/objects/info/alternates &&
+ test_commit C fileC one &&
+ git fsck >out &&
+ ! grep "missing blob" out
+ )
+'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index cd9231c..b2ddf5a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -12,7 +12,7 @@
only the updates to dir/sub.
Also tested are "git add -u" without limiting, and "git add -u"
-without contents changes.'
+without contents changes, and other conditions'
. ./test-lib.sh
@@ -128,4 +128,52 @@
'
+test_expect_success 'add -u resolves unmerged paths' '
+ git reset --hard &&
+ one=$(echo 1 | git hash-object -w --stdin) &&
+ two=$(echo 2 | git hash-object -w --stdin) &&
+ three=$(echo 3 | git hash-object -w --stdin) &&
+ {
+ for path in path1 path2
+ do
+ echo "100644 $one 1 $path"
+ echo "100644 $two 2 $path"
+ echo "100644 $three 3 $path"
+ done
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } |
+ git update-index --index-info &&
+ echo 3 >path1 &&
+ echo 2 >path3 &&
+ echo 2 >path5 &&
+ git add -u &&
+ git ls-files -s "path?" >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } >expect &&
+ test_cmp expect actual &&
+
+ # Bonus tests. Explicit resolving
+ git add path3 path5 &&
+ test_must_fail git add path4 &&
+ test_must_fail git add path6 &&
+ git rm path4 &&
+ git rm path6 &&
+
+ git ls-files -s "path?" >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $two 0 path3"
+ echo "100644 $two 0 path5"
+ } >expect
+
+'
+
test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
new file mode 100755
index 0000000..9393a25
--- /dev/null
+++ b/t/t3301-notes.sh
@@ -0,0 +1,95 @@
+#!/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
new file mode 100755
index 0000000..00d27bf
--- /dev/null
+++ b/t/t3302-notes-index-expensive.sh
@@ -0,0 +1,98 @@
+#!/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/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 57a3cad..5869061 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -6,10 +6,6 @@
'
. ./test-lib.sh
-# we always run the interactive rebases unchanged, so just disable the editor
-GIT_EDITOR=:
-export GIT_EDITOR
-
log_with_names () {
git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
git name-rev --stdin --name-only --refs=refs/heads/$1
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 796f795..5e65afa 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -257,4 +257,37 @@
test -z "$(git diff second)"
'
+test_expect_success 'am --committer-date-is-author-date' '
+ git checkout first &&
+ test_tick &&
+ git am --committer-date-is-author-date patch1 &&
+ git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+ test "$at" = "$ct"
+'
+
+test_expect_success 'am without --committer-date-is-author-date' '
+ git checkout first &&
+ test_tick &&
+ git am patch1 &&
+ git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+ test "$at" != "$ct"
+'
+
+# This checks for +0000 because TZ is set to UTC and that should
+# show up when the current time is used. The date in message is set
+# by test_tick that uses -0700 timezone; if this feature does not
+# work, we will see that instead of +0000.
+test_expect_success 'am --ignore-date' '
+ git checkout first &&
+ test_tick &&
+ git am --ignore-date patch1 &&
+ git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ echo "$at" | grep "+0000"
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 1f59960..bc5b7ce 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -107,6 +107,32 @@
)
'
+test_expect_success 'remove remote protects non-remote branches' '
+(
+ cd test &&
+ (cat >expect1 <<EOF
+Note: A non-remote branch was not removed; to delete it, use:
+ git branch -d master
+EOF
+ cat >expect2 <<EOF
+Note: Non-remote branches were not removed; to delete them, use:
+ git branch -d foobranch
+ git branch -d master
+EOF
+) &&
+ git tag footag
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual1 &&
+ git branch foobranch &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual2 &&
+ git branch -d foobranch &&
+ git tag -d footag &&
+ test_cmp expect1 actual1 &&
+ test_cmp expect2 actual2
+)
+'
+
cat > test/expect << EOF
* remote origin
URL: $(pwd)/one
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4426df9..89649e7 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -492,7 +492,7 @@
git checkout master &&
git config receive.denyCurrentBranch warn) &&
git push testrepo master 2>stderr &&
- grep "warning.*this may cause confusion" stderr
+ grep "warning: updating the current branch" stderr
'
test_expect_success 'deny push to HEAD of non-bare repository' '
@@ -510,7 +510,7 @@
git config receive.denyCurrentBranch true &&
git config core.bare true) &&
git push testrepo master 2>stderr &&
- ! grep "warning.*this may cause confusion" stderr
+ ! grep "warning: updating the current branch" stderr
'
test_expect_success 'allow push to HEAD of non-bare repository (config)' '
@@ -520,7 +520,7 @@
git config receive.denyCurrentBranch false
) &&
git push testrepo master 2>stderr &&
- ! grep "warning.*this may cause confusion" stderr
+ ! grep "warning: updating the current branch" stderr
'
test_expect_success 'fetch with branches' '
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index ef2e78f..8fb3a56 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -55,9 +55,17 @@
git mv -k untracked1 untracked2 path0 &&
test -f untracked1 &&
test -f untracked2 &&
- test ! -f path0/untracked1
+ test ! -f path0/untracked1 &&
test ! -f path0/untracked2'
+test_expect_success \
+ 'checking -f on untracked file with existing target' \
+ 'touch path0/untracked1 &&
+ git mv -f untracked1 path0
+ test ! -f .git/index.lock &&
+ test -f untracked1 &&
+ test -f path0/untracked1'
+
# clean up the mess in case bad things happen
rm -f idontexist untracked1 untracked2 \
path0/idontexist path0/untracked1 path0/untracked2 \
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 8537bf9..cb04743 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -39,7 +39,9 @@
'
test_expect_success 'rewrite bare repository identically' '
- (git config core.bare true && cd .git && git filter-branch branch)
+ (git config core.bare true && cd .git &&
+ git filter-branch branch > filter-output 2>&1 &&
+ ! fgrep fatal filter-output)
'
git config core.bare false
test_expect_success 'result is really identical' '
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 09fa5f1..e768c3e 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -9,38 +9,81 @@
. ./test-lib.sh
+# All the mergetool test work by checking out a temporary branch based
+# off 'branch1' and then merging in master and checking the results of
+# running mergetool
+
test_expect_success 'setup' '
echo master >file1 &&
- git add file1 &&
+ mkdir subdir &&
+ echo master sub >subdir/file3 &&
+ git add file1 subdir/file3 &&
git commit -m "added file1" &&
+
git checkout -b branch1 master &&
echo branch1 change >file1 &&
echo branch1 newfile >file2 &&
- git add file1 file2 &&
+ echo branch1 sub >subdir/file3 &&
+ git add file1 file2 subdir/file3 &&
git commit -m "branch1 changes" &&
- git checkout -b branch2 master &&
- echo branch2 change >file1 &&
- echo branch2 newfile >file2 &&
- git add file1 file2 &&
- git commit -m "branch2 changes" &&
+
git checkout master &&
echo master updated >file1 &&
echo master new >file2 &&
- git add file1 file2 &&
- git commit -m "master updates"
+ echo master new sub >subdir/file3 &&
+ git add file1 file2 subdir/file3 &&
+ git commit -m "master updates" &&
+
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true
'
test_expect_success 'custom mergetool' '
- git config merge.tool mytool &&
- git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
- git config mergetool.mytool.trustExitCode true &&
- git checkout branch1 &&
+ git checkout -b test1 branch1 &&
test_must_fail git merge master >/dev/null 2>&1 &&
- ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
- ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
test "$(cat file1)" = "master updated" &&
test "$(cat file2)" = "master new" &&
- git commit -m "branch1 resolved with mergetool"
+ test "$(cat subdir/file3)" = "master new sub" &&
+ git commit -m "branch1 resolved with mergetool"
'
+test_expect_success 'mergetool crlf' '
+ git config core.autocrlf true &&
+ git checkout -b test2 branch1
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
+ test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
+ test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+ git commit -m "branch1 resolved with mergetool - autocrlf" &&
+ git config core.autocrlf false &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool in subdir' '
+ git checkout -b test3 branch1
+ cd subdir && (
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+ test "$(cat file3)" = "master new sub" )
+'
+
+# We can't merge files from parent directories when running mergetool
+# from a subdir. Is this a bug?
+#
+#test_expect_failure 'mergetool in subdir' '
+# cd subdir && (
+# ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+# ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+# test "$(cat ../file1)" = "master updated" &&
+# test "$(cat ../file2)" = "master new" &&
+# git commit -m "branch1 resolved with mergetool - subdir" )
+#'
+
test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index c1839f7..6f6244a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -201,7 +201,7 @@
# Both <file> and <contents> default to <message>.
test_commit () {
- file=${2:-$(echo "$1" | tr 'A-Z' 'a-z')}
+ file=${2:-"$1.t"}
echo "${3-$1}" > "$file" &&
git add "$file" &&
test_tick &&
diff --git a/dump-cache-tree.c b/test-dump-cache-tree.c
similarity index 100%
rename from dump-cache-tree.c
rename to test-dump-cache-tree.c
diff --git a/unpack-file.c b/unpack-file.c
index 6dd8ad0..75cd2f1 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -29,7 +29,7 @@
git_extract_argv0_path(argv[0]);
if (argc != 2)
- usage("git-unpack-file <sha1>");
+ usage("git unpack-file <sha1>");
if (get_sha1(argv[1], sha1))
die("Not a valid object name %s", argv[1]);
diff --git a/upload-pack.c b/upload-pack.c
index 5db6f93..19c24db 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -11,7 +11,7 @@
#include "list-objects.h"
#include "run-command.h"
-static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
+static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>";
/* bits #0..7 in revision.h, #8..10 in commit.c */
#define THEY_HAVE (1u << 11)
diff --git a/utf8.c b/utf8.c
index dc37353..ddfdc5e 100644
--- a/utf8.c
+++ b/utf8.c
@@ -246,6 +246,25 @@
return git_wcwidth(ch);
}
+/*
+ * Returns the total number of columns required by a null-terminated
+ * string, assuming that the string is utf8. Returns strlen() instead
+ * if the string does not look like a valid utf8 string.
+ */
+int utf8_strwidth(const char *string)
+{
+ int width = 0;
+ const char *orig = string;
+
+ while (1) {
+ if (!string)
+ return strlen(orig);
+ if (!*string)
+ return width;
+ width += utf8_width(&string, NULL);
+ }
+}
+
int is_utf8(const char *text)
{
while (*text) {
diff --git a/utf8.h b/utf8.h
index 98cce1b..2f1b14f 100644
--- a/utf8.h
+++ b/utf8.h
@@ -5,6 +5,7 @@
ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
int utf8_width(const char **start, size_t *remainder_p);
+int utf8_strwidth(const char *string);
int is_utf8(const char *text);
int is_encoding_utf8(const char *name);