aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.6.0.4.txt2
-rw-r--r--Documentation/RelNotes-1.6.1.txt6
-rw-r--r--Documentation/config.txt139
-rw-r--r--Documentation/git-add.txt2
-rw-r--r--Documentation/git-cherry-pick.txt13
-rw-r--r--Documentation/git-commit.txt2
-rw-r--r--Documentation/git-send-email.txt28
-rw-r--r--Documentation/git-svn.txt2
-rw-r--r--Documentation/merge-config.txt14
-rw-r--r--Documentation/merge-options.txt8
-rw-r--r--Documentation/technical/api-strbuf.txt7
-rw-r--r--Documentation/user-manual.txt2
-rw-r--r--Makefile2
-rw-r--r--builtin-checkout.c2
-rw-r--r--builtin-clone.c109
-rw-r--r--builtin-commit.c8
-rw-r--r--builtin-diff-files.c4
-rw-r--r--builtin-diff.c13
-rw-r--r--builtin-fast-export.c4
-rw-r--r--builtin-fetch.c19
-rw-r--r--builtin-merge.c21
-rw-r--r--builtin-remote.c8
-rw-r--r--cache.h3
-rw-r--r--config.c5
-rwxr-xr-xcontrib/completion/git-completion.bash62
-rw-r--r--contrib/emacs/git.el337
-rwxr-xr-xcontrib/fast-import/git-p42
-rw-r--r--daemon.c111
-rw-r--r--environment.c3
-rwxr-xr-xgit-pull.sh10
-rwxr-xr-xgit-send-email.perl255
-rw-r--r--interpolate.c103
-rw-r--r--interpolate.h26
-rw-r--r--ll-merge.c21
-rw-r--r--merge-recursive.c1
-rw-r--r--parse-options.c22
-rw-r--r--parse-options.h6
-rw-r--r--preload-index.c91
-rw-r--r--sha1_file.c5
-rw-r--r--strbuf.c16
-rw-r--r--strbuf.h5
-rwxr-xr-xt/t4020-diff-external.sh21
-rwxr-xr-xt/t4030-diff-textconv.sh2
-rwxr-xr-xt/t5521-pull-options.sh60
-rwxr-xr-xt/t9001-send-email.sh8
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh2
-rwxr-xr-xt/t9301-fast-export.sh8
-rw-r--r--xdiff-interface.c11
48 files changed, 942 insertions, 669 deletions
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt
index fba3f30a8..d522661d3 100644
--- a/Documentation/RelNotes-1.6.0.4.txt
+++ b/Documentation/RelNotes-1.6.0.4.txt
@@ -30,7 +30,7 @@ Fixes since v1.6.0.3
* 'git status' incorrectly reported a submodule directory as an untracked
directory.
-* 'git svn' used deprecated 'git-foo' form of subcommand invocaition.
+* 'git svn' used deprecated 'git-foo' form of subcommand invocation.
* 'git update-ref -d' to remove a reference did not honor --no-deref option.
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt
index 7fdf83f60..848541a5a 100644
--- a/Documentation/RelNotes-1.6.1.txt
+++ b/Documentation/RelNotes-1.6.1.txt
@@ -55,9 +55,9 @@ on.
to a non-zero value to accept the suggestion when git can uniquely
guess.
-* The packfile machinery hopefully is more robust when dealilng with
+* The packfile machinery hopefully is more robust when dealing with
corrupt packs if redundant objects involved in the corruption are
- available elsehwere.
+ available elsewhere.
* "git add -N path..." adds the named paths as an empty blob, so that
subsequent "git diff" will show a diff as if they are creation events.
@@ -157,7 +157,7 @@ on.
* "git log" learned "--source" to show what ref each commit was reached
from.
-* "git log" also learned "--simplify-by-decration" to show the
+* "git log" also learned "--simplify-by-decoration" to show the
birds-eye-view of the topology of the history.
* "git log --pretty=format:" learned "%d" format element that inserts
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d53673221..b233fe535 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -413,6 +413,15 @@ data writes properly, but can be useful for filesystems that do not use
journalling (traditional UNIX filesystems) or that only journal metadata
and not file contents (OS X's HFS+, or Linux ext3 with "data=writeback").
+core.preloadindex::
+ Enable parallel index preload for operations like 'git diff'
++
+This can speed up operations like 'git diff' and 'git status' especially
+on filesystems like NFS that have weak caching semantics and thus
+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.
+
alias.*::
Command aliases for the linkgit:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
@@ -572,9 +581,6 @@ color.status.<slot>::
to red). The values of these variables may be specified as in
color.branch.<slot>.
-commit.template::
- Specify a file to use as the template for new commit messages.
-
color.ui::
When set to `always`, always use colors in all git commands which
are capable of colored output. When false (or `never`), never. When
@@ -582,6 +588,9 @@ color.ui::
terminal. When more specific variables of color.* are set, they always
take precedence over this setting. Defaults to false.
+commit.template::
+ Specify a file to use as the template for new commit messages.
+
diff.autorefreshindex::
When using 'git-diff' to compare with work tree
files, do not consider stat-only change as changed.
@@ -714,18 +723,6 @@ gc.rerereunresolved::
kept for this many days when 'git-rerere gc' is run.
The default is 15 days. See linkgit:git-rerere[1].
-rerere.autoupdate::
- When set to true, `git-rerere` updates the index with the
- resulting contents after it cleanly resolves conflicts using
- previously recorded resolution. Defaults to false.
-
-rerere.enabled::
- Activate recording of resolved conflicts, so that identical
- conflict hunks can be resolved automatically, should they
- be encountered again. linkgit:git-rerere[1] command is by
- default enabled if you create `rr-cache` directory under
- `$GIT_DIR`, but can be disabled by setting this option to false.
-
gitcvs.enabled::
Whether the CVS server interface is enabled for this repository.
See linkgit:git-cvsserver[1].
@@ -832,7 +829,7 @@ gui.fastcopyblame::
repositories at the expense of less thorough copy detection.
gui.copyblamethreshold::
- Specifies the theshold to use in 'git gui blame' original location
+ Specifies the threshold to use in 'git gui blame' original location
detection, measured in alphanumeric characters. See the
linkgit:git-blame[1] manual for more information on copy detection.
@@ -917,6 +914,10 @@ i18n.logOutputEncoding::
Character encoding the commit messages are converted to when
running 'git-log' and friends.
+imap::
+ The configuration variables in the 'imap' section are described
+ in linkgit:git-imap-send[1].
+
instaweb.browser::
Specify the program that will be used to browse your working
repository in gitweb. See linkgit:git-instaweb[1].
@@ -952,8 +953,6 @@ man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].
-include::merge-config.txt[]
-
man.<tool>.cmd::
Specify the command to invoke the specified man viewer. The
specified command is evaluated in shell with the man page
@@ -963,13 +962,7 @@ man.<tool>.path::
Override the path for the given tool that may be used to
display help in the 'man' format. See linkgit:git-help[1].
-merge.conflictstyle::
- Specify the style in which conflicted hunks are written out to
- working tree files upon merge. The default is "merge", which
- shows `<<<<<<<` conflict marker, change made by one side,
- `=======` marker, change made by the other side, and then
- `>>>>>>>` marker. An alternate style, "diff3", adds `|||||||`
- marker and the original text before `=======` marker.
+include::merge-config.txt[]
mergetool.<tool>.path::
Override the path for the given tool. This is useful in case
@@ -1079,6 +1072,41 @@ pull.octopus::
pull.twohead::
The default merge strategy to use when pulling a single branch.
+receive.fsckObjects::
+ If it is set to true, git-receive-pack will check all received
+ objects. It will abort in the case of a malformed object or a
+ broken link. The result of an abort are only dangling objects.
+ Defaults to false.
+
+receive.unpackLimit::
+ If the number of objects received in a push is below this
+ limit then the objects will be unpacked into loose object
+ files. However if the number of received objects equals or
+ exceeds this limit then the received pack will be stored as
+ a pack, after adding any missing delta bases. Storing the
+ pack from a push can make the push operation complete faster,
+ especially on slow filesystems. If not set, the value of
+ `transfer.unpackLimit` is used instead.
+
+receive.denyDeletes::
+ If set to true, git-receive-pack will deny a ref update that deletes
+ the ref. Use this to prevent such a ref deletion via a push.
+
+receive.denyCurrentBranch::
+ If set to true or "refuse", receive-pack will deny a ref update
+ to the currently checked out branch of a non-bare repository.
+ Such a push is potentially dangerous because it brings the HEAD
+ out of sync with the index and working tree. If set to "warn",
+ print a warning of such a push to stderr, but allow the push to
+ proceed. If set to false or "ignore", allow such pushes with no
+ message. Defaults to "warn".
+
+receive.denyNonFastForwards::
+ If set to true, git-receive-pack will deny a ref update which is
+ not a fast forward. Use this to prevent such an update via a push,
+ even if that push is forced. This configuration variable is
+ set when initializing a shared repository.
+
remote.<name>.url::
The URL of a remote repository. See linkgit:git-fetch[1] or
linkgit:git-push[1].
@@ -1128,6 +1156,18 @@ repack.usedeltabaseoffset::
"false" and repack. Access from old git versions over the
native protocol are unaffected by this option.
+rerere.autoupdate::
+ When set to true, `git-rerere` updates the index with the
+ resulting contents after it cleanly resolves conflicts using
+ previously recorded resolution. Defaults to false.
+
+rerere.enabled::
+ Activate recording of resolved conflicts, so that identical
+ conflict hunks can be resolved automatically, should they
+ be encountered again. linkgit:git-rerere[1] command is by
+ default enabled if you create `rr-cache` directory under
+ `$GIT_DIR`, but can be disabled by setting this option to false.
+
showbranch.default::
The default set of branches for linkgit:git-show-branch[1].
See linkgit:git-show-branch[1].
@@ -1164,6 +1204,11 @@ tar.umask::
archiving user's umask will be used instead. See umask(2) and
linkgit:git-archive[1].
+transfer.unpackLimit::
+ When `fetch.unpackLimit` or `receive.unpackLimit` are
+ not set, the value of this variable is used instead.
+ The default value is 100.
+
url.<base>.insteadOf::
Any URL that starts with this value will be rewritten to
start, instead, with <base>. In cases where some site serves a
@@ -1192,50 +1237,6 @@ user.signingkey::
unchanged to gpg's --local-user parameter, so you may specify a key
using any method that gpg supports.
-imap::
- The configuration variables in the 'imap' section are described
- in linkgit:git-imap-send[1].
-
-receive.fsckObjects::
- If it is set to true, git-receive-pack will check all received
- objects. It will abort in the case of a malformed object or a
- broken link. The result of an abort are only dangling objects.
- Defaults to false.
-
-receive.unpackLimit::
- If the number of objects received in a push is below this
- limit then the objects will be unpacked into loose object
- files. However if the number of received objects equals or
- exceeds this limit then the received pack will be stored as
- a pack, after adding any missing delta bases. Storing the
- pack from a push can make the push operation complete faster,
- especially on slow filesystems. If not set, the value of
- `transfer.unpackLimit` is used instead.
-
-receive.denyDeletes::
- If set to true, git-receive-pack will deny a ref update that deletes
- the ref. Use this to prevent such a ref deletion via a push.
-
-receive.denyNonFastForwards::
- If set to true, git-receive-pack will deny a ref update which is
- not a fast forward. Use this to prevent such an update via a push,
- even if that push is forced. This configuration variable is
- set when initializing a shared repository.
-
-receive.denyCurrentBranch::
- If set to true or "refuse", receive-pack will deny a ref update
- to the currently checked out branch of a non-bare repository.
- Such a push is potentially dangerous because it brings the HEAD
- out of sync with the index and working tree. If set to "warn",
- print a warning of such a push to stderr, but allow the push to
- proceed. If set to false or "ignore", allow such pushes with no
- message. Defaults to "warn".
-
-transfer.unpackLimit::
- When `fetch.unpackLimit` or `receive.unpackLimit` are
- not set, the value of this variable is used instead.
- The default value is 100.
-
web.browser::
Specify a web browser that may be used by some commands.
Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 6fc20b0ba..7c129cb24 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -98,7 +98,7 @@ OPTIONS
Record only the fact that the path will be added later. An entry
for the path is placed in the index with no content. This is
useful for, among other things, showing the unstaged content of
- such files with 'git diff' and commiting them with 'git commit
+ such files with 'git diff' and committing them with 'git commit
-a'.
--refresh::
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 837fb08b7..b764130d2 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -55,13 +55,12 @@ OPTIONS
-n::
--no-commit::
- Usually the command automatically creates a commit with
- a commit log message stating which commit was
- cherry-picked. This flag applies the change necessary
- to cherry-pick the named commit to your working tree
- and the index, but does not make the commit. In addition,
- when this option is used, your index does not have to match
- the HEAD commit. The cherry-pick is done against the
+ Usually the command automatically creates a commit.
+ This flag applies the change necessary to cherry-pick
+ the named commit to your working tree and the index,
+ but does not make the commit. In addition, when this
+ option is used, your index does not have to match the
+ HEAD commit. The cherry-pick is done against the
beginning state of your index.
+
This is useful when cherry-picking more than one commits'
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a1ce9a8bf..6203461f4 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -95,7 +95,7 @@ OPTIONS
-s::
--signoff::
- Add Signed-off-by line by the commiter at the end of the commit
+ Add Signed-off-by line by the committer at the end of the commit
log message.
-n::
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 82f505686..acf8bf41d 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
SYNOPSIS
--------
-'git send-email' [options] <file|directory> [... file|directory]
+'git send-email' [options] <file|directory|rev-list options>...
DESCRIPTION
@@ -37,9 +37,23 @@ The --bcc option must be repeated for each user you want on the bcc list.
+
The --cc option must be repeated for each user you want on the cc list.
+--annotate::
+ Review each patch you're about to send in an editor. The setting
+ 'sendemail.multiedit' defines if this will spawn one editor per patch
+ or one for all of them at once.
+
--compose::
Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
introductory message for the patch series.
++
+When compose is in used, git send-email gets less interactive will use the
+values of the headers you set there. If the body of the email (what you type
+after the headers and a blank line) only contains blank (or GIT: prefixed)
+lines, the summary won't be sent, but git-send-email will still use the
+Headers values if you don't removed them.
++
+If it wasn't able to see a header in the summary it will ask you about it
+interactively after quitting your editor.
--from::
Specify the sender of the emails. This will default to
@@ -183,6 +197,12 @@ Administering
--[no-]validate::
Perform sanity checks on patches.
Currently, validation means the following:
+
+--[no-]format-patch::
+ When an argument may be understood either as a reference or as a file name,
+ choose to understand it as a format-patch argument ('--format-patch')
+ or as a file name ('--no-format-patch'). By default, when such a conflict
+ occurs, git send-email will fail.
+
--
* Warn of patches that contain lines longer than 998 characters; this
@@ -204,6 +224,12 @@ sendemail.aliasfiletype::
Format of the file(s) specified in sendemail.aliasesfile. Must be
one of 'mutt', 'mailrc', 'pine', or 'gnus'.
+sendemail.multiedit::
+ If true (default), a single editor instance will be spawned to edit
+ files you have to edit (patches when '--annotate' is used, and the
+ summary when '--compose' is used). If false, files will be edited one
+ after the other, spawning a new editor each time.
+
Author
------
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index ba94cd17d..8d0c421b8 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -109,7 +109,7 @@ COMMANDS
This works similarly to `svn update` or 'git-pull' except that
it preserves linear history with 'git-rebase' instead of
-'git-merge' for ease of dcommiting with 'git-svn'.
+'git-merge' for ease of dcommitting with 'git-svn'.
This accepts all options that 'git-svn fetch' and 'git-rebase'
accept. However, '--fetch-all' only fetches from the current
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
index c735788b0..86d5b26ab 100644
--- a/Documentation/merge-config.txt
+++ b/Documentation/merge-config.txt
@@ -1,6 +1,10 @@
-merge.stat::
- Whether to print the diffstat between ORIG_HEAD and the merge result
- at the end of the merge. True by default.
+merge.conflictstyle::
+ Specify the style in which conflicted hunks are written out to
+ working tree files upon merge. The default is "merge", which
+ shows `<<<<<<<` conflict marker, change made by one side,
+ `=======` marker, change made by the other side, and then
+ `>>>>>>>` marker. An alternate style, "diff3", adds `|||||||`
+ marker and the original text before `=======` marker.
merge.log::
Whether to include summaries of merged commits in newly created
@@ -11,6 +15,10 @@ merge.renameLimit::
during a merge; if not specified, defaults to the value of
diff.renameLimit.
+merge.stat::
+ Whether to print the diffstat between ORIG_HEAD and the merge result
+ at the end of the merge. True by default.
+
merge.tool::
Controls which merge resolution program is used by
linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 007909a82..427cdefd0 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -1,3 +1,11 @@
+-q::
+--quiet::
+ Operate quietly.
+
+-v::
+--verbose::
+ Be verbose.
+
--stat::
Show a diffstat at the end of the merge. The diffstat is also
controlled by the configuration option merge.stat.
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index a9668e5f2..a8ee2fe6a 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -205,6 +205,13 @@ In order to facilitate caching and to make it possible to give
parameters to the callback, `strbuf_expand()` passes a context pointer,
which can be used by the programmer of the callback as she sees fit.
+`strbuf_expand_dict_cb`::
+
+ Used as callback for `strbuf_expand()`, expects an array of
+ struct strbuf_expand_dict_entry as context, i.e. pairs of
+ placeholder and replacement string. The array needs to be
+ terminated by an entry with placeholder set to NULL.
+
`strbuf_addf`::
Add a formatted string to the buffer.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index c0d8caf46..da9c6b299 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -546,7 +546,7 @@ $ git bisect skip
-------------------------------------------------
In this case, though, git may not eventually be able to tell the first
-bad one between some first skipped commits and a latter bad commit.
+bad one between some first skipped commits and a later bad commit.
There are also ways to automate the bisecting process if you have a
test script that can tell a good from a bad commit. See
diff --git a/Makefile b/Makefile
index 35adafa01..649cfb819 100644
--- a/Makefile
+++ b/Makefile
@@ -437,7 +437,6 @@ LIB_OBJS += grep.o
LIB_OBJS += hash.o
LIB_OBJS += help.o
LIB_OBJS += ident.o
-LIB_OBJS += interpolate.o
LIB_OBJS += levenshtein.o
LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
@@ -496,6 +495,7 @@ 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
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 464fd2570..7f3bd7bb1 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -553,7 +553,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
describe_detached_head("Previous HEAD position was", old.commit);
- if (!old.commit) {
+ if (!old.commit && !opts->force) {
if (!opts->quiet) {
fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
diff --git a/builtin-clone.c b/builtin-clone.c
index 8e1a1d399..2feac9c5c 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -134,9 +134,9 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
}
if (is_bare) {
- char *result = xmalloc(end - start + 5);
- sprintf(result, "%.*s.git", (int)(end - start), start);
- return result;
+ struct strbuf result = STRBUF_INIT;
+ strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
+ return strbuf_detach(&result, 0);
}
return xstrndup(start, end - start);
@@ -183,36 +183,38 @@ static void setup_reference(const char *repo)
free(ref_git_copy);
}
-static void copy_or_link_directory(char *src, char *dest)
+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
{
struct dirent *de;
struct stat buf;
int src_len, dest_len;
DIR *dir;
- dir = opendir(src);
+ dir = opendir(src->buf);
if (!dir)
- die("failed to open %s\n", src);
+ die("failed to open %s\n", src->buf);
- if (mkdir(dest, 0777)) {
+ if (mkdir(dest->buf, 0777)) {
if (errno != EEXIST)
- die("failed to create directory %s\n", dest);
- else if (stat(dest, &buf))
- die("failed to stat %s\n", dest);
+ die("failed to create directory %s\n", dest->buf);
+ else if (stat(dest->buf, &buf))
+ die("failed to stat %s\n", dest->buf);
else if (!S_ISDIR(buf.st_mode))
- die("%s exists and is not a directory\n", dest);
+ die("%s exists and is not a directory\n", dest->buf);
}
- src_len = strlen(src);
- src[src_len] = '/';
- dest_len = strlen(dest);
- dest[dest_len] = '/';
+ strbuf_addch(src, '/');
+ src_len = src->len;
+ strbuf_addch(dest, '/');
+ dest_len = dest->len;
while ((de = readdir(dir)) != NULL) {
- strcpy(src + src_len + 1, de->d_name);
- strcpy(dest + dest_len + 1, de->d_name);
- if (stat(src, &buf)) {
- warning ("failed to stat %s\n", src);
+ strbuf_setlen(src, src_len);
+ strbuf_addstr(src, de->d_name);
+ strbuf_setlen(dest, dest_len);
+ strbuf_addstr(dest, de->d_name);
+ if (stat(src->buf, &buf)) {
+ warning ("failed to stat %s\n", src->buf);
continue;
}
if (S_ISDIR(buf.st_mode)) {
@@ -221,17 +223,17 @@ static void copy_or_link_directory(char *src, char *dest)
continue;
}
- if (unlink(dest) && errno != ENOENT)
- die("failed to unlink %s\n", dest);
+ if (unlink(dest->buf) && errno != ENOENT)
+ die("failed to unlink %s\n", dest->buf);
if (!option_no_hardlinks) {
- if (!link(src, dest))
+ if (!link(src->buf, dest->buf))
continue;
if (option_local)
- die("failed to create link %s\n", dest);
+ die("failed to create link %s\n", dest->buf);
option_no_hardlinks = 1;
}
- if (copy_file(dest, src, 0666))
- die("failed to copy file to %s\n", dest);
+ if (copy_file(dest->buf, src->buf, 0666))
+ die("failed to copy file to %s\n", dest->buf);
}
closedir(dir);
}
@@ -240,17 +242,19 @@ static const struct ref *clone_local(const char *src_repo,
const char *dest_repo)
{
const struct ref *ret;
- char src[PATH_MAX];
- char dest[PATH_MAX];
+ struct strbuf src = STRBUF_INIT;
+ struct strbuf dest = STRBUF_INIT;
struct remote *remote;
struct transport *transport;
if (option_shared)
add_to_alternates_file(src_repo);
else {
- snprintf(src, PATH_MAX, "%s/objects", src_repo);
- snprintf(dest, PATH_MAX, "%s/objects", dest_repo);
- copy_or_link_directory(src, dest);
+ strbuf_addf(&src, "%s/objects", src_repo);
+ strbuf_addf(&dest, "%s/objects", dest_repo);
+ copy_or_link_directory(&src, &dest);
+ strbuf_release(&src);
+ strbuf_release(&dest);
}
remote = remote_get(src_repo);
@@ -354,8 +358,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
- char branch_top[256], key[256], value[256];
- struct strbuf reflog_msg = STRBUF_INIT;
+ struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
+ struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL;
char *src_ref_prefix = "refs/heads/";
@@ -459,35 +463,36 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_bare) {
if (option_mirror)
src_ref_prefix = "refs/";
- strcpy(branch_top, src_ref_prefix);
+ strbuf_addstr(&branch_top, src_ref_prefix);
git_config_set("core.bare", "true");
} else {
- snprintf(branch_top, sizeof(branch_top),
- "refs/remotes/%s/", option_origin);
+ strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
}
if (option_mirror || !option_bare) {
/* Configure the remote */
if (option_mirror) {
- snprintf(key, sizeof(key),
- "remote.%s.mirror", option_origin);
- git_config_set(key, "true");
+ strbuf_addf(&key, "remote.%s.mirror", option_origin);
+ git_config_set(key.buf, "true");
+ strbuf_reset(&key);
}
- snprintf(key, sizeof(key), "remote.%s.url", option_origin);
- git_config_set(key, repo);
+ strbuf_addf(&key, "remote.%s.url", option_origin);
+ git_config_set(key.buf, repo);
+ strbuf_reset(&key);
- snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
- snprintf(value, sizeof(value),
- "+%s*:%s*", src_ref_prefix, branch_top);
- git_config_set_multivar(key, value, "^$", 0);
+ strbuf_addf(&key, "remote.%s.fetch", option_origin);
+ strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
+ git_config_set_multivar(key.buf, value.buf, "^$", 0);
+ strbuf_reset(&key);
+ strbuf_reset(&value);
}
refspec.force = 0;
refspec.pattern = 1;
refspec.src = src_ref_prefix;
- refspec.dst = branch_top;
+ refspec.dst = branch_top.buf;
if (path && !is_bundle)
refs = clone_local(path, git_dir);
@@ -541,7 +546,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
head_points_at->old_sha1,
NULL, 0, DIE_ON_ERR);
- strbuf_addstr(&head_ref, branch_top);
+ strbuf_addstr(&head_ref, branch_top.buf);
strbuf_addstr(&head_ref, "HEAD");
/* Remote branch link */
@@ -549,10 +554,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
head_points_at->peer_ref->name,
reflog_msg.buf);
- snprintf(key, sizeof(key), "branch.%s.remote", head);
- git_config_set(key, option_origin);
- snprintf(key, sizeof(key), "branch.%s.merge", head);
- git_config_set(key, head_points_at->name);
+ 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);
}
} else if (remote_head) {
/* Source had detached HEAD pointing somewhere. */
@@ -602,6 +608,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
strbuf_release(&reflog_msg);
+ strbuf_release(&branch_top);
+ strbuf_release(&key);
+ strbuf_release(&value);
junk_pid = 0;
return 0;
}
diff --git a/builtin-commit.c b/builtin-commit.c
index 591d16b91..1677e6b45 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -225,18 +225,18 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
if (interactive) {
interactive_add(argc, argv, prefix);
- if (read_cache() < 0)
+ if (read_cache_preload(NULL) < 0)
die("index file corrupt");
commit_style = COMMIT_AS_IS;
return get_index_file();
}
- if (read_cache() < 0)
- die("index file corrupt");
-
if (*argv)
pathspec = get_pathspec(prefix, argv);
+ if (read_cache_preload(pathspec) < 0)
+ die("index file corrupt");
+
/*
* Non partial, non as-is commit.
*
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index 2b578c714..5b64011de 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -59,8 +59,8 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
rev.combine_merges = rev.dense_combined_merges = 1;
- if (read_cache() < 0) {
- perror("read_cache");
+ if (read_cache_preload(rev.diffopt.paths) < 0) {
+ perror("read_cache_preload");
return -1;
}
result = run_diff_files(&rev, options);
diff --git a/builtin-diff.c b/builtin-diff.c
index 7ceceeb6f..dddcf697d 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -134,8 +134,8 @@ static int builtin_diff_index(struct rev_info *revs,
revs->max_count != -1 || revs->min_age != -1 ||
revs->max_age != -1)
usage(builtin_diff_usage);
- if (read_cache() < 0) {
- perror("read_cache");
+ if (read_cache_preload(revs->diffopt.paths) < 0) {
+ perror("read_cache_preload");
return -1;
}
return run_diff_index(revs, cached);
@@ -234,8 +234,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
revs->combine_merges = revs->dense_combined_merges = 1;
setup_work_tree();
- if (read_cache() < 0) {
- perror("read_cache");
+ if (read_cache_preload(revs->diffopt.paths) < 0) {
+ perror("read_cache_preload");
return -1;
}
result = run_diff_files(revs, options);
@@ -290,6 +290,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
/* Otherwise, we are doing the usual "git" diff */
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
+ /* Default to let external be used */
+ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+
if (nongit)
die("Not a git repository");
argc = setup_revisions(argc, argv, &rev, NULL);
@@ -298,7 +301,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
- DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index 7c93eb878..7d5d57ad7 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -354,7 +354,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
case OBJ_TAG:
tag = (struct tag *)e->item;
while (tag && tag->object.type == OBJ_TAG) {
- string_list_insert(full_name, extra_refs)->util = tag;
+ string_list_append(full_name, extra_refs)->util = tag;
tag = (struct tag *)tag->tagged;
}
if (!tag)
@@ -374,7 +374,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
}
if (commit->util)
/* more than one name for the same object */
- string_list_insert(full_name, extra_refs)->util = commit;
+ string_list_append(full_name, extra_refs)->util = commit;
else
commit->util = full_name;
}
diff --git a/builtin-fetch.c b/builtin-fetch.c
index f151cfa2f..7568163af 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -22,7 +22,7 @@ enum {
TAGS_SET = 2
};
-static int append, force, keep, update_head_ok, verbose, quiet;
+static int append, force, keep, update_head_ok, verbosity;
static int tags = TAGS_DEFAULT;
static const char *depth;
static const char *upload_pack;
@@ -30,8 +30,7 @@ static struct strbuf default_rla = STRBUF_INIT;
static struct transport *transport;
static struct option builtin_fetch_options[] = {
- OPT__QUIET(&quiet),
- OPT__VERBOSE(&verbose),
+ OPT__VERBOSITY(&verbosity),
OPT_BOOLEAN('a', "append", &append,
"append to .git/FETCH_HEAD instead of overwriting"),
OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
@@ -192,7 +191,6 @@ static int s_update_ref(const char *action,
static int update_local_ref(struct ref *ref,
const char *remote,
- int verbose,
char *display)
{
struct commit *current = NULL, *updated;
@@ -210,7 +208,7 @@ static int update_local_ref(struct ref *ref,
die("object %s not found", sha1_to_hex(ref->new_sha1));
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
- if (verbose)
+ if (verbosity > 0)
sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
"[up to date]", REFCOL_WIDTH, remote,
pretty_ref);
@@ -366,18 +364,19 @@ static int store_updated_refs(const char *url, const char *remote_name,
note);
if (ref)
- rc |= update_local_ref(ref, what, verbose, note);
+ rc |= update_local_ref(ref, what, note);
else
sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
SUMMARY_WIDTH, *kind ? kind : "branch",
REFCOL_WIDTH, *what ? what : "HEAD");
if (*note) {
- if (!shown_url) {
+ if (verbosity >= 0 && !shown_url) {
fprintf(stderr, "From %.*s\n",
url_len, url);
shown_url = 1;
}
- fprintf(stderr, " %s\n", note);
+ if (verbosity >= 0)
+ fprintf(stderr, " %s\n", note);
}
}
fclose(fp);
@@ -637,9 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
remote = remote_get(argv[0]);
transport = transport_get(remote, remote->url[0]);
- if (verbose >= 2)
+ if (verbosity >= 2)
transport->verbose = 1;
- if (quiet)
+ if (verbosity < 0)
transport->verbose = -1;
if (upload_pack)
set_option(TRANS_OPT_UPLOADPACK, upload_pack);
diff --git a/builtin-merge.c b/builtin-merge.c
index 5e7910bd8..7c2b90c70 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -50,6 +50,7 @@ static unsigned char head[20], stash[20];
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
static const char *branch;
+static int verbosity;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -171,6 +172,7 @@ static struct option builtin_merge_options[] = {
OPT_CALLBACK('m', "message", &merge_msg, "message",
"message to be used for the merge commit (if any)",
option_parse_message),
+ OPT__VERBOSITY(&verbosity),
OPT_END()
};
@@ -250,7 +252,8 @@ static void restore_state(void)
/* This is called when no merge was necessary. */
static void finish_up_to_date(const char *msg)
{
- printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+ if (verbosity >= 0)
+ printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
drop_save();
}
@@ -331,14 +334,15 @@ static void finish(const unsigned char *new_head, const char *msg)
if (!msg)
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
else {
- printf("%s\n", msg);
+ if (verbosity >= 0)
+ printf("%s\n", msg);
strbuf_addf(&reflog_message, "%s: %s",
getenv("GIT_REFLOG_ACTION"), msg);
}
if (squash) {
squash_message();
} else {
- if (!merge_msg.len)
+ if (verbosity >= 0 && !merge_msg.len)
printf("No merge message -- not updating HEAD\n");
else {
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
@@ -872,6 +876,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, builtin_merge_options,
builtin_merge_usage, 0);
+ if (verbosity < 0)
+ show_diffstat = 0;
if (squash) {
if (!allow_fast_forward)
@@ -1013,10 +1019,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
- printf("Updating %s..%s\n",
- hex,
- find_unique_abbrev(remoteheads->item->object.sha1,
- DEFAULT_ABBREV));
+ if (verbosity >= 0)
+ printf("Updating %s..%s\n",
+ hex,
+ find_unique_abbrev(remoteheads->item->object.sha1,
+ DEFAULT_ABBREV));
strbuf_addstr(&msg, "Fast forward");
if (have_message)
strbuf_addstr(&msg,
diff --git a/builtin-remote.c b/builtin-remote.c
index 14774e36c..abc8dd838 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -14,7 +14,7 @@ static const char * const builtin_remote_usage[] = {
"git remote rm <name>",
"git remote show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
- "git remote update [group]",
+ "git remote [-v | --verbose] update [group]",
NULL
};
@@ -42,7 +42,11 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
static int fetch_remote(const char *name)
{
- const char *argv[] = { "fetch", name, NULL };
+ const char *argv[] = { "fetch", name, NULL, NULL };
+ if (verbose) {
+ argv[1] = "-v";
+ argv[2] = name;
+ }
printf("Updating %s\n", name);
if (run_command_v_opt(argv, RUN_GIT_CMD))
return error("Could not fetch %s", name);
diff --git a/cache.h b/cache.h
index 7efba7756..487e5e1b1 100644
--- a/cache.h
+++ b/cache.h
@@ -262,6 +262,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
#define read_cache() read_index(&the_index)
#define read_cache_from(path) read_index_from(&the_index, (path))
+#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
#define is_cache_unborn() is_index_unborn(&the_index)
#define read_cache_unmerged() read_index_unmerged(&the_index)
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
@@ -368,6 +369,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
/* Initialize and use the cache information */
extern int read_index(struct index_state *);
+extern int read_index_preload(struct index_state *, const char **pathspec);
extern int read_index_from(struct index_state *, const char *path);
extern int is_index_unborn(struct index_state *);
extern int read_index_unmerged(struct index_state *);
@@ -458,6 +460,7 @@ extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
extern int auto_crlf;
extern int fsync_object_files;
+extern int core_preload_index;
enum safe_crlf {
SAFE_CRLF_FALSE = 0,
diff --git a/config.c b/config.c
index 67cc1dcad..d2fc8f5f2 100644
--- a/config.c
+++ b/config.c
@@ -490,6 +490,11 @@ static int git_default_core_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.preloadindex")) {
+ core_preload_index = git_config_bool(var, value);
+ return 0;
+ }
+
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index de193ba7c..c79c98ffe 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -188,11 +188,22 @@ __git_tags ()
__git_refs ()
{
- local cmd i is_hash=y dir="$(__gitdir "$1")"
+ local i is_hash=y dir="$(__gitdir "$1")"
+ local cur="${COMP_WORDS[COMP_CWORD]}" format refs
if [ -d "$dir" ]; then
- if [ -e "$dir/HEAD" ]; then echo HEAD; fi
- git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
- refs/tags refs/heads refs/remotes
+ case "$cur" in
+ refs|refs/*)
+ format="refname"
+ refs="${cur%/*}"
+ ;;
+ *)
+ if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+ format="refname:short"
+ refs="refs/tags refs/heads refs/remotes"
+ ;;
+ esac
+ git --git-dir="$dir" for-each-ref --format="%($format)" \
+ $refs
return
fi
for i in $(git ls-remote "$dir" 2>/dev/null); do
@@ -636,21 +647,12 @@ _git_branch ()
_git_bundle ()
{
- local mycword="$COMP_CWORD"
- case "${COMP_WORDS[0]}" in
- git)
- local cmd="${COMP_WORDS[2]}"
- mycword="$((mycword-1))"
- ;;
- git-bundle*)
- local cmd="${COMP_WORDS[1]}"
- ;;
- esac
- case "$mycword" in
- 1)
+ local cmd="${COMP_WORDS[2]}"
+ case "$COMP_CWORD" in
+ 2)
__gitcomp "create list-heads verify unbundle"
;;
- 2)
+ 3)
# looking for a file
;;
*)
@@ -798,12 +800,7 @@ _git_fetch ()
__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
;;
*)
- local remote
- case "${COMP_WORDS[0]}" in
- git-fetch) remote="${COMP_WORDS[1]}" ;;
- git) remote="${COMP_WORDS[2]}" ;;
- esac
- __gitcomp "$(__git_refs2 "$remote")"
+ __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
;;
esac
fi
@@ -1047,12 +1044,7 @@ _git_pull ()
if [ "$COMP_CWORD" = 2 ]; then
__gitcomp "$(__git_remotes)"
else
- local remote
- case "${COMP_WORDS[0]}" in
- git-pull) remote="${COMP_WORDS[1]}" ;;
- git) remote="${COMP_WORDS[2]}" ;;
- esac
- __gitcomp "$(__git_refs "$remote")"
+ __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
fi
}
@@ -1065,19 +1057,13 @@ _git_push ()
else
case "$cur" in
*:*)
- local remote
- case "${COMP_WORDS[0]}" in
- git-push) remote="${COMP_WORDS[1]}" ;;
- git) remote="${COMP_WORDS[2]}" ;;
- esac
-
local pfx=""
case "$COMP_WORDBREAKS" in
*:*) : great ;;
*) pfx="${cur%%:*}:" ;;
esac
- __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}"
+ __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
;;
+*)
__gitcomp "$(__git_refs)" + "${cur#+}"
@@ -1357,7 +1343,7 @@ _git_revert ()
return
;;
esac
- COMPREPLY=()
+ __gitcomp "$(__git_refs)"
}
_git_rm ()
@@ -1579,7 +1565,7 @@ _git_tag ()
-m|-F)
COMPREPLY=()
;;
- -*|tag|git-tag)
+ -*|tag)
if [ $f = 1 ]; then
__gitcomp "$(__git_tags)"
else
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index c1cf1cbcc..09e8bae3a 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -173,7 +173,7 @@ if there is already one that displays the same directory."
(defconst git-log-msg-separator "--- log message follows this line ---")
(defvar git-log-edit-font-lock-keywords
- `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$"
+ `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$"
(1 font-lock-keyword-face)
(2 font-lock-function-name-face))
(,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
@@ -183,11 +183,9 @@ if there is already one that displays the same directory."
"Build a list of NAME=VALUE strings from a list of environment strings."
(mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
-(defun git-call-process-env (buffer env &rest args)
+(defun git-call-process (buffer &rest args)
"Wrapper for call-process that sets environment strings."
- (let ((process-environment (append (git-get-env-strings env)
- process-environment)))
- (apply #'call-process "git" nil buffer nil args)))
+ (apply #'call-process "git" nil buffer nil args))
(defun git-call-process-display-error (&rest args)
"Wrapper for call-process that displays error messages."
@@ -197,17 +195,26 @@ if there is already one that displays the same directory."
(let ((default-directory dir)
(buffer-read-only nil))
(erase-buffer)
- (eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
+ (eq 0 (apply #'git-call-process (list buffer t) args))))))
(unless ok (display-message-or-buffer buffer))
ok))
-(defun git-call-process-env-string (env &rest args)
- "Wrapper for call-process that sets environment strings,
-and returns the process output as a string, or nil if the git failed."
+(defun git-call-process-string (&rest args)
+ "Wrapper for call-process that returns the process output as a string,
+or nil if the git command failed."
(with-temp-buffer
- (and (eq 0 (apply #' git-call-process-env t env args))
+ (and (eq 0 (apply #'git-call-process t args))
(buffer-string))))
+(defun git-call-process-string-display-error (&rest args)
+ "Wrapper for call-process that displays error message and returns
+the process output as a string, or nil if the git command failed."
+ (with-temp-buffer
+ (if (eq 0 (apply #'git-call-process (list t t) args))
+ (buffer-string)
+ (display-message-or-buffer (current-buffer))
+ nil)))
+
(defun git-run-process-region (buffer start end program args)
"Run a git process with a buffer region as input."
(let ((output-buffer (current-buffer))
@@ -226,7 +233,7 @@ and returns the process output as a string, or nil if the git failed."
(let ((default-directory dir)
(buffer-read-only nil))
(erase-buffer)
- (apply #'git-call-process-env buffer nil args)))
+ (apply #'git-call-process buffer args)))
(message "Running git %s...done" (car args))
buffer))
@@ -327,7 +334,7 @@ and returns the process output as a string, or nil if the git failed."
(let ((cdup (with-output-to-string
(with-current-buffer standard-output
(cd dir)
- (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup"))
+ (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup"))
(error "cannot find top-level git tree for %s." dir))))))
(expand-file-name (concat (file-name-as-directory dir)
(car (split-string cdup "\n"))))))
@@ -348,8 +355,8 @@ and returns the process output as a string, or nil if the git failed."
(sort-lines nil (point-min) (point-max))
(save-buffer))
(when created
- (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
- (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
+ (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+ (git-update-status-files (list (file-relative-name ignore-name)))))
; propertize definition for XEmacs, stolen from erc-compat
(eval-when-compile
@@ -367,38 +374,41 @@ and returns the process output as a string, or nil if the git failed."
(defun git-rev-parse (rev)
"Parse a revision name and return its SHA1."
(git-get-string-sha1
- (git-call-process-env-string nil "rev-parse" rev)))
+ (git-call-process-string "rev-parse" rev)))
(defun git-config (key)
"Retrieve the value associated to KEY in the git repository config file."
- (let ((str (git-call-process-env-string nil "config" key)))
+ (let ((str (git-call-process-string "config" key)))
(and str (car (split-string str "\n")))))
(defun git-symbolic-ref (ref)
"Wrapper for the git-symbolic-ref command."
- (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
+ (let ((str (git-call-process-string "symbolic-ref" ref)))
(and str (car (split-string str "\n")))))
(defun git-update-ref (ref newval &optional oldval reason)
"Update a reference by calling git-update-ref."
(let ((args (and oldval (list oldval))))
- (push newval args)
+ (when newval (push newval args))
(push ref args)
(when reason
(push reason args)
(push "-m" args))
+ (unless newval (push "-d" args))
(apply 'git-call-process-display-error "update-ref" args)))
(defun git-read-tree (tree &optional index-file)
"Read a tree into the index file."
- (apply #'git-call-process-env nil
- (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
- "read-tree" (if tree (list tree))))
+ (let ((process-environment
+ (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+ (apply 'git-call-process-display-error "read-tree" (if tree (list tree)))))
(defun git-write-tree (&optional index-file)
"Call git-write-tree and return the resulting tree SHA1 as a string."
- (git-get-string-sha1
- (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
+ (let ((process-environment
+ (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+ (git-get-string-sha1
+ (git-call-process-string-display-error "write-tree"))))
(defun git-commit-tree (buffer tree head)
"Call git-commit-tree with buffer as input and return the resulting commit SHA1."
@@ -424,11 +434,11 @@ and returns the process output as a string, or nil if the git failed."
(when (re-search-forward "^Date: +\\(.*\\)$" nil t)
(setq author-date (match-string 1)))
(goto-char (point-min))
- (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t)
- (unless (string-equal head (match-string 1))
- (setq subject "commit (merge): ")
+ (when (re-search-forward "^Merge: +\\(.*\\)" nil t)
+ (setq subject "commit (merge): ")
+ (dolist (parent (split-string (match-string 1) " +" t))
(push "-p" args)
- (push (match-string 1) args))))
+ (push parent args))))
(setq log-start (point-min)))
(setq log-end (point-max))
(goto-char log-start)
@@ -452,7 +462,7 @@ and returns the process output as a string, or nil if the git failed."
(defun git-empty-db-p ()
"Check if the git db is empty (no commit done yet)."
- (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD"))))
+ (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD"))))
(defun git-get-merge-heads ()
"Retrieve the merge heads from the MERGE_HEAD file if present."
@@ -468,7 +478,7 @@ and returns the process output as a string, or nil if the git failed."
(defun git-get-commit-description (commit)
"Get a one-line description of COMMIT."
(let ((coding-system-for-read (git-get-logoutput-coding-system)))
- (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
+ (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit)))
(if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
(concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
descr))))
@@ -487,14 +497,11 @@ and returns the process output as a string, or nil if the git failed."
old-perm new-perm ;; permission flags
rename-state ;; rename or copy state
orig-name ;; original name for renames or copies
+ needs-update ;; whether file needs to be updated
needs-refresh) ;; whether file needs to be refreshed
(defvar git-status nil)
-(defun git-clear-status (status)
- "Remove everything from the status list."
- (ewoc-filter status (lambda (info) nil)))
-
(defun git-set-fileinfo-state (info state)
"Set the state of a file info."
(unless (eq (git-fileinfo->state info) state)
@@ -502,6 +509,7 @@ and returns the process output as a string, or nil if the git failed."
(git-fileinfo->new-perm info) (git-fileinfo->old-perm info)
(git-fileinfo->rename-state info) nil
(git-fileinfo->orig-name info) nil
+ (git-fileinfo->needs-update info) nil
(git-fileinfo->needs-refresh info) t)))
(defun git-status-filenames-map (status func files &rest args)
@@ -511,10 +519,11 @@ and returns the process output as a string, or nil if the git failed."
(let ((file (pop files))
(node (ewoc-nth status 0)))
(while (and file node)
- (let ((info (ewoc-data node)))
- (if (string-lessp (git-fileinfo->name info) file)
+ (let* ((info (ewoc-data node))
+ (name (git-fileinfo->name info)))
+ (if (string-lessp name file)
(setq node (ewoc-next status node))
- (if (string-equal (git-fileinfo->name info) file)
+ (if (string-equal name file)
(apply func info args))
(setq file (pop files))))))))
@@ -612,39 +621,52 @@ and returns the process output as a string, or nil if the git failed."
(git-file-type-as-string old-perm new-perm)
(git-rename-as-string info)))))
-(defun git-insert-info-list (status infolist)
- "Insert a list of file infos in the status buffer, replacing existing ones if any."
- (setq infolist (sort infolist
- (lambda (info1 info2)
- (string-lessp (git-fileinfo->name info1)
- (git-fileinfo->name info2)))))
- (let ((info (pop infolist))
- (node (ewoc-nth status 0)))
+(defun git-update-node-fileinfo (node info)
+ "Update the fileinfo of the specified node. The names are assumed to match already."
+ (let ((data (ewoc-data node)))
+ (setf
+ ;; preserve the marked flag
+ (git-fileinfo->marked info) (git-fileinfo->marked data)
+ (git-fileinfo->needs-update data) nil)
+ (when (not (equal info data))
+ (setf (git-fileinfo->needs-refresh info) t
+ (ewoc-data node) info))))
+
+(defun git-insert-info-list (status infolist files)
+ "Insert a sorted list of file infos in the status buffer, replacing existing ones if any."
+ (let* ((info (pop infolist))
+ (node (ewoc-nth status 0))
+ (name (and info (git-fileinfo->name info)))
+ remaining)
(while info
- (cond ((not node)
- (setq node (ewoc-enter-last status info))
- (setq info (pop infolist)))
- ((string-lessp (git-fileinfo->name (ewoc-data node))
- (git-fileinfo->name info))
- (setq node (ewoc-next status node)))
- ((string-equal (git-fileinfo->name (ewoc-data node))
- (git-fileinfo->name info))
- ;; preserve the marked flag
- (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
- (setf (git-fileinfo->needs-refresh info) t)
- (setf (ewoc-data node) info)
- (setq info (pop infolist)))
- (t
- (setq node (ewoc-enter-before status node info))
- (setq info (pop infolist)))))))
+ (let ((nodename (and node (git-fileinfo->name (ewoc-data node)))))
+ (while (and files (string-lessp (car files) name))
+ (push (pop files) remaining))
+ (when (and files (string-equal (car files) name))
+ (setq files (cdr files)))
+ (cond ((not nodename)
+ (setq node (ewoc-enter-last status info))
+ (setq info (pop infolist))
+ (setq name (and info (git-fileinfo->name info))))
+ ((string-lessp nodename name)
+ (setq node (ewoc-next status node)))
+ ((string-equal nodename name)
+ ;; preserve the marked flag
+ (git-update-node-fileinfo node info)
+ (setq info (pop infolist))
+ (setq name (and info (git-fileinfo->name info))))
+ (t
+ (setq node (ewoc-enter-before status node info))
+ (setq info (pop infolist))
+ (setq name (and info (git-fileinfo->name info)))))))
+ (nconc (nreverse remaining) files)))
(defun git-run-diff-index (status files)
"Run git-diff-index on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
- (let ((remaining (copy-sequence files))
- infolist)
+ (let (infolist)
(with-temp-buffer
- (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+ (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files)
(goto-char (point-min))
(while (re-search-forward
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
@@ -659,11 +681,12 @@ Return the list of files that haven't been handled."
(push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
(push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
(push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
- (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
- (setq remaining (delete name remaining))
- (when new-name (setq remaining (delete new-name remaining))))))
- (git-insert-info-list status infolist)
- remaining))
+ (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)))))
+ (setq infolist (sort (nreverse infolist)
+ (lambda (info1 info2)
+ (string-lessp (git-fileinfo->name info1)
+ (git-fileinfo->name info2)))))
+ (git-insert-info-list status infolist files)))
(defun git-find-status-file (status file)
"Find a given file in the status ewoc and return its node."
@@ -677,38 +700,35 @@ Return the list of files that haven't been handled."
Return the list of files that haven't been handled."
(let (infolist)
(with-temp-buffer
- (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
+ (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files))
(goto-char (point-min))
(while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
(let ((name (match-string 1)))
(push (git-create-fileinfo default-state name 0
(if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
- infolist)
- (setq files (delete name files)))))
- (git-insert-info-list status infolist)
- files))
+ infolist))))
+ (setq infolist (nreverse infolist)) ;; assume it is sorted already
+ (git-insert-info-list status infolist files)))
(defun git-run-ls-files-cached (status files default-state)
"Run git-ls-files -c on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
- (let ((remaining (copy-sequence files))
- infolist)
+ (let (infolist)
(with-temp-buffer
- (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files)
+ (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files)
(goto-char (point-min))
(while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
(let* ((new-perm (string-to-number (match-string 1) 8))
(old-perm (if (eq default-state 'added) 0 new-perm))
(name (match-string 2)))
- (push (git-create-fileinfo default-state name old-perm new-perm) infolist)
- (setq remaining (delete name remaining)))))
- (git-insert-info-list status infolist)
- remaining))
+ (push (git-create-fileinfo default-state name old-perm new-perm) infolist))))
+ (setq infolist (nreverse infolist)) ;; assume it is sorted already
+ (git-insert-info-list status infolist files)))
(defun git-run-ls-unmerged (status files)
"Run git-ls-files -u on FILES and parse the results into STATUS."
(with-temp-buffer
- (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
+ (apply #'git-call-process t "ls-files" "-z" "-u" "--" files)
(goto-char (point-min))
(let (unmerged-files)
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
@@ -732,11 +752,17 @@ Return the list of files that haven't been handled."
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
(append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
-(defun git-update-status-files (files &optional default-state)
+(defun git-update-status-files (&optional files mark-files)
"Update the status of FILES from the index."
(unless git-status (error "Not in git-status buffer."))
- (when (or git-show-uptodate files)
- (git-run-ls-files-cached git-status files 'uptodate))
+ ;; set the needs-update flag on existing files
+ (if (setq files (sort files #'string-lessp))
+ (git-status-filenames-map
+ git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files)
+ (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status)
+ (git-call-process nil "update-index" "--refresh")
+ (when git-show-uptodate
+ (git-run-ls-files-cached git-status nil 'uptodate)))
(let* ((remaining-files
(if (git-empty-db-p) ; we need some special handling for an empty db
(git-run-ls-files-cached git-status files 'added)
@@ -746,13 +772,17 @@ Return the list of files that haven't been handled."
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
(when (or remaining-files (and git-show-ignored (not files)))
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
- (git-set-filenames-state git-status remaining-files default-state)
+ (unless files
+ (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update))))
+ (when remaining-files
+ (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate)))
+ (git-set-filenames-state git-status remaining-files nil)
+ (when mark-files (git-mark-files git-status files))
(git-refresh-files)
(git-refresh-ewoc-hf git-status)))
(defun git-mark-files (status files)
"Mark all the specified FILES, and unmark the others."
- (setq files (sort files #'string-lessp))
(let ((file (and files (pop files)))
(node (ewoc-nth status 0)))
(while node
@@ -824,19 +854,18 @@ Return the list of files that haven't been handled."
(defun git-update-index (index-file files)
"Run git-update-index on a list of files."
- (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
+ (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file)))
+ process-environment))
added deleted modified)
(dolist (info files)
(case (git-fileinfo->state info)
('added (push info added))
('deleted (push info deleted))
('modified (push info modified))))
- (when added
- (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
- (when deleted
- (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
- (when modified
- (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
+ (and
+ (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added)))
+ (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted)))
+ (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified))))))
(defun git-run-pre-commit-hook ()
"Run the pre-commit hook if any."
@@ -862,33 +891,30 @@ Return the list of files that haven't been handled."
(message "You cannot commit unmerged files, resolve them first.")
(unwind-protect
(let ((files (git-marked-files-state 'added 'deleted 'modified))
- head head-tree)
+ head tree head-tree)
(unless (git-empty-db-p)
(setq head (git-rev-parse "HEAD")
head-tree (git-rev-parse "HEAD^{tree}")))
- (if files
- (progn
- (message "Running git commit...")
- (git-read-tree head-tree index-file)
- (git-update-index nil files) ;update both the default index
- (git-update-index index-file files) ;and the temporary one
- (let ((tree (git-write-tree index-file)))
- (if (or (not (string-equal tree head-tree))
- (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
- (let ((commit (git-commit-tree buffer tree head)))
- (when commit
- (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
- (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
- (with-current-buffer buffer (erase-buffer))
- (git-update-status-files (git-get-filenames files) 'uptodate)
- (git-call-process-env nil nil "rerere")
- (git-call-process-env nil nil "gc" "--auto")
- (git-refresh-files)
- (git-refresh-ewoc-hf git-status)
- (message "Committed %s." commit)
- (git-run-hook "post-commit" nil)))
- (message "Commit aborted."))))
- (message "No files to commit.")))
+ (message "Running git commit...")
+ (when
+ (and
+ (git-read-tree head-tree index-file)
+ (git-update-index nil files) ;update both the default index
+ (git-update-index index-file files) ;and the temporary one
+ (setq tree (git-write-tree index-file)))
+ (if (or (not (string-equal tree head-tree))
+ (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
+ (let ((commit (git-commit-tree buffer tree head)))
+ (when commit
+ (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+ (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+ (with-current-buffer buffer (erase-buffer))
+ (git-update-status-files (git-get-filenames files))
+ (git-call-process nil "rerere")
+ (git-call-process nil "gc" "--auto")
+ (message "Committed %s." commit)
+ (git-run-hook "post-commit" nil)))
+ (message "Commit aborted."))))
(delete-file index-file))))))
@@ -990,6 +1016,11 @@ Return the list of files that haven't been handled."
(setq node (ewoc-prev git-status node)))
(ewoc-goto-node git-status last)))
+(defun git-insert-file (file)
+ "Insert file(s) into the git-status buffer."
+ (interactive "fInsert file: ")
+ (git-update-status-files (list (file-relative-name file))))
+
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
@@ -998,7 +1029,7 @@ Return the list of files that haven't been handled."
(unless files
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
(when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
- (git-update-status-files files 'uptodate)
+ (git-update-status-files files)
(git-success-message "Added" files))))
(defun git-ignore-file ()
@@ -1008,7 +1039,7 @@ Return the list of files that haven't been handled."
(unless files
(push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
(dolist (f files) (git-append-to-ignore f))
- (git-update-status-files files 'ignored)
+ (git-update-status-files files)
(git-success-message "Ignored" files)))
(defun git-remove-file ()
@@ -1026,7 +1057,7 @@ Return the list of files that haven't been handled."
(delete-directory name)
(delete-file name))))
(when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
- (git-update-status-files files nil)
+ (git-update-status-files files)
(git-success-message "Removed" files)))
(message "Aborting"))))
@@ -1054,7 +1085,7 @@ Return the list of files that haven't been handled."
(apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
(or (not modified)
(apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
- (git-update-status-files (append added modified) 'uptodate)
+ (git-update-status-files (append added modified))
(when ok
(dolist (file modified)
(let ((buffer (get-file-buffer file)))
@@ -1067,7 +1098,7 @@ Return the list of files that haven't been handled."
(let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
(when files
(when (apply 'git-call-process-display-error "update-index" "--" files)
- (git-update-status-files files 'uptodate)
+ (git-update-status-files files)
(git-success-message "Resolved" files)))))
(defun git-remove-handled ()
@@ -1225,11 +1256,10 @@ Return the list of files that haven't been handled."
(goto-char (point-max))
(insert sign-off "\n"))))
-(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg)
+(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg)
"Setup the log buffer for a commit."
(unless git-status (error "Not in git-status buffer."))
- (let ((merge-heads (git-get-merge-heads))
- (dir default-directory)
+ (let ((dir default-directory)
(committer-name (git-get-committer-name))
(committer-email (git-get-committer-email))
(sign-off git-append-signed-off-by))
@@ -1243,9 +1273,8 @@ Return the list of files that haven't been handled."
(or author-email committer-email)
(if date (format "Date: %s\n" date) "")
(if merge-heads
- (format "Parent: %s\n%s\n"
- (git-rev-parse "HEAD")
- (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n"))
+ (format "Merge: %s\n"
+ (mapconcat 'identity merge-heads " "))
""))
'face 'git-header-face)
(propertize git-log-msg-separator 'face 'git-separator-face)
@@ -1285,7 +1314,7 @@ Return the list of files that haven't been handled."
(goto-char (point-min))
(when (re-search-forward "^Date: \\(.*\\)$" nil t)
(setq date (match-string 1)))))
- (git-setup-log-buffer buffer author-name author-email subject date))
+ (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date))
(if (boundp 'log-edit-diff-function)
(log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
(log-edit-diff-function . git-log-edit-diff)) buffer)
@@ -1296,11 +1325,13 @@ Return the list of files that haven't been handled."
(defun git-setup-commit-buffer (commit)
"Setup the commit buffer with the contents of COMMIT."
- (let (author-name author-email subject date msg)
+ (let (parents author-name author-email subject date msg)
(with-temp-buffer
(let ((coding-system (git-get-logoutput-coding-system)))
- (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
+ (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit)
(goto-char (point-min))
+ (when (re-search-forward "^Merge: *\\(.*\\)$" nil t)
+ (setq parents (cdr (split-string (match-string 1) " +"))))
(when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
(setq author-name (match-string 1))
(setq author-email (match-string 2)))
@@ -1312,14 +1343,14 @@ Return the list of files that haven't been handled."
(setq subject (pop msg))
(while (and msg (zerop (length (car msg))) (pop msg)))))
(git-setup-log-buffer (get-buffer-create "*git-commit*")
- author-name author-email subject date
+ parents author-name author-email subject date
(mapconcat #'identity msg "\n"))))
(defun git-get-commit-files (commit)
"Retrieve the list of files modified by COMMIT."
(let (files)
(with-temp-buffer
- (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
+ (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit)
(goto-char (point-min))
(while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
(push (match-string 1) files)))
@@ -1333,10 +1364,11 @@ amended version of it."
(when (git-empty-db-p) (error "No commit to amend."))
(let* ((commit (git-rev-parse "HEAD"))
(files (git-get-commit-files commit)))
- (when (git-call-process-display-error "reset" "--soft" "HEAD^")
- (git-update-status-files (copy-sequence files) 'uptodate)
- (git-mark-files git-status files)
- (git-refresh-files)
+ (when (if (git-rev-parse "HEAD^")
+ (git-call-process-display-error "reset" "--soft" "HEAD^")
+ (and (git-update-ref "ORIG_HEAD" commit)
+ (git-update-ref "HEAD" nil commit)))
+ (git-update-status-files files t)
(git-setup-commit-buffer commit)
(git-commit-file))))
@@ -1377,27 +1409,10 @@ amended version of it."
(defun git-refresh-status ()
"Refresh the git status buffer."
(interactive)
- (let* ((status git-status)
- (pos (ewoc-locate status))
- (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
- (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
- (unless status (error "Not in git-status buffer."))
- (message "Refreshing git status...")
- (git-call-process-env nil nil "update-index" "--refresh")
- (git-clear-status status)
- (git-update-status-files nil)
- ; restore file marks
- (when marked-files
- (git-status-filenames-map status
- (lambda (info)
- (setf (git-fileinfo->marked info) t)
- (setf (git-fileinfo->needs-refresh info) t))
- marked-files)
- (git-refresh-files))
- ; move point to the current file name if any
- (message "Refreshing git status...done")
- (let ((node (and cur-name (git-find-status-file status cur-name))))
- (when node (ewoc-goto-node status node)))))
+ (unless git-status (error "Not in git-status buffer."))
+ (message "Refreshing git status...")
+ (git-update-status-files)
+ (message "Refreshing git status...done"))
(defun git-status-quit ()
"Quit git-status mode."
@@ -1434,6 +1449,7 @@ amended version of it."
(define-key map "\r" 'git-find-file)
(define-key map "g" 'git-refresh-status)
(define-key map "i" 'git-ignore-file)
+ (define-key map "I" 'git-insert-file)
(define-key map "l" 'git-log-file)
(define-key map "m" 'git-mark-file)
(define-key map "M" 'git-mark-all)
@@ -1490,6 +1506,7 @@ amended version of it."
["Revert File" git-revert-file t]
["Ignore File" git-ignore-file t]
["Remove File" git-remove-file t]
+ ["Insert File" git-insert-file t]
"--------"
["Find File" git-find-file t]
["View File" git-view-file t]
@@ -1576,8 +1593,8 @@ Meant to be used in `after-save-hook'."
(let ((filename (file-relative-name file dir)))
; skip files located inside the .git directory
(unless (string-match "^\\.git/" filename)
- (git-call-process-env nil nil "add" "--refresh" "--" filename)
- (git-update-status-files (list filename) 'uptodate)))))))
+ (git-call-process nil "add" "--refresh" "--" filename)
+ (git-update-status-files (list filename))))))))
(defun git-help ()
"Display help for Git mode."
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 9f0a5f92c..b44fbfc9b 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -981,7 +981,7 @@ class P4Sync(Command):
if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
- text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
+ text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
contents[stat['depotFile']] = text
diff --git a/daemon.c b/daemon.c
index b9ba44c58..1cef3098d 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,7 +1,6 @@
#include "cache.h"
#include "pkt-line.h"
#include "exec_cmd.h"
-#include "interpolate.h"
#include <syslog.h>
@@ -54,26 +53,10 @@ static const char *user_path;
static unsigned int timeout;
static unsigned int init_timeout;
-/*
- * Static table for now. Ugh.
- * Feel free to make dynamic as needed.
- */
-#define INTERP_SLOT_HOST (0)
-#define INTERP_SLOT_CANON_HOST (1)
-#define INTERP_SLOT_IP (2)
-#define INTERP_SLOT_PORT (3)
-#define INTERP_SLOT_DIR (4)
-#define INTERP_SLOT_PERCENT (5)
-
-static struct interp interp_table[] = {
- { "%H", 0},
- { "%CH", 0},
- { "%IP", 0},
- { "%P", 0},
- { "%D", 0},
- { "%%", 0},
-};
-
+static char *hostname;
+static char *canon_hostname;
+static char *ip_address;
+static char *tcp_port;
static void logreport(int priority, const char *err, va_list params)
{
@@ -163,7 +146,7 @@ static int avoid_alias(char *p)
}
}
-static char *path_ok(struct interp *itable)
+static char *path_ok(char *directory)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
@@ -171,7 +154,7 @@ static char *path_ok(struct interp *itable)
char *path;
char *dir;
- dir = itable[INTERP_SLOT_DIR].value;
+ dir = directory;
if (avoid_alias(dir)) {
logerror("'%s': aliased", dir);
@@ -201,14 +184,27 @@ static char *path_ok(struct interp *itable)
}
}
else if (interpolated_path && saw_extended_args) {
+ struct strbuf expanded_path = STRBUF_INIT;
+ struct strbuf_expand_dict_entry dict[] = {
+ { "H", hostname },
+ { "CH", canon_hostname },
+ { "IP", ip_address },
+ { "P", tcp_port },
+ { "D", directory },
+ { "%", "%" },
+ { NULL }
+ };
+
if (*dir != '/') {
/* Allow only absolute */
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
return NULL;
}
- interpolate(interp_path, PATH_MAX, interpolated_path,
- interp_table, ARRAY_SIZE(interp_table));
+ strbuf_expand(&expanded_path, interpolated_path,
+ strbuf_expand_dict_cb, &dict);
+ strlcpy(interp_path, expanded_path.buf, PATH_MAX);
+ strbuf_release(&expanded_path);
loginfo("Interpolated dir '%s'", interp_path);
dir = interp_path;
@@ -233,7 +229,7 @@ static char *path_ok(struct interp *itable)
* prefixing the base path
*/
if (base_path && base_path_relaxed && !retried_path) {
- dir = itable[INTERP_SLOT_DIR].value;
+ dir = directory;
retried_path = 1;
continue;
}
@@ -299,14 +295,12 @@ static int git_daemon_config(const char *var, const char *value, void *cb)
return 0;
}
-static int run_service(struct interp *itable, struct daemon_service *service)
+static int run_service(char *dir, struct daemon_service *service)
{
const char *path;
int enabled = service->enabled;
- loginfo("Request %s for '%s'",
- service->name,
- itable[INTERP_SLOT_DIR].value);
+ loginfo("Request %s for '%s'", service->name, dir);
if (!enabled && !service->overridable) {
logerror("'%s': service not enabled.", service->name);
@@ -314,7 +308,7 @@ static int run_service(struct interp *itable, struct daemon_service *service)
return -1;
}
- if (!(path = path_ok(itable)))
+ if (!(path = path_ok(dir)))
return -1;
/*
@@ -413,13 +407,13 @@ static void make_service_overridable(const char *name, int ena)
/*
* Separate the "extra args" information as supplied by the client connection.
- * Any resulting data is squirreled away in the given interpolation table.
*/
-static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
+static void parse_extra_args(char *extra_args, int buflen)
{
char *val;
int vallen;
char *end = extra_args + buflen;
+ char *hp;
while (extra_args < end && *extra_args) {
saw_extended_args = 1;
@@ -433,25 +427,22 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
if (port) {
*port = 0;
port++;
- interp_set_entry(table, INTERP_SLOT_PORT, port);
+ free(tcp_port);
+ tcp_port = xstrdup(port);
}
- interp_set_entry(table, INTERP_SLOT_HOST, host);
+ free(hostname);
+ hostname = xstrdup(host);
}
/* On to the next one */
extra_args = val + vallen;
}
}
-}
-
-static void fill_in_extra_table_entries(struct interp *itable)
-{
- char *hp;
/*
* Replace literal host with lowercase-ized hostname.
*/
- hp = interp_table[INTERP_SLOT_HOST].value;
+ hp = hostname;
if (!hp)
return;
for ( ; *hp; hp++)
@@ -470,17 +461,17 @@ static void fill_in_extra_table_entries(struct interp *itable)
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
- gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0);
+ gai = getaddrinfo(hostname, 0, &hints, &ai0);
if (!gai) {
for (ai = ai0; ai; ai = ai->ai_next) {
struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
inet_ntop(AF_INET, &sin_addr->sin_addr,
addrbuf, sizeof(addrbuf));
- interp_set_entry(interp_table,
- INTERP_SLOT_CANON_HOST, ai->ai_canonname);
- interp_set_entry(interp_table,
- INTERP_SLOT_IP, addrbuf);
+ free(canon_hostname);
+ canon_hostname = xstrdup(ai->ai_canonname);
+ free(ip_address);
+ ip_address = xstrdup(addrbuf);
break;
}
freeaddrinfo(ai0);
@@ -493,7 +484,7 @@ static void fill_in_extra_table_entries(struct interp *itable)
char **ap;
static char addrbuf[HOST_NAME_MAX + 1];
- hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
+ hent = gethostbyname(hostname);
ap = hent->h_addr_list;
memset(&sa, 0, sizeof sa);
@@ -504,8 +495,10 @@ static void fill_in_extra_table_entries(struct interp *itable)
inet_ntop(hent->h_addrtype, &sa.sin_addr,
addrbuf, sizeof(addrbuf));
- interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name);
- interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf);
+ free(canon_hostname);
+ canon_hostname = xstrdup(hent->h_name);
+ free(ip_address);
+ ip_address = xstrdup(addrbuf);
}
#endif
}
@@ -557,16 +550,14 @@ static int execute(struct sockaddr *addr)
pktlen--;
}
- /*
- * Initialize the path interpolation table for this connection.
- */
- interp_clear_table(interp_table, ARRAY_SIZE(interp_table));
- interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%");
+ free(hostname);
+ free(canon_hostname);
+ free(ip_address);
+ free(tcp_port);
+ hostname = canon_hostname = ip_address = tcp_port = NULL;
- if (len != pktlen) {
- parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
- fill_in_extra_table_entries(interp_table);
- }
+ if (len != pktlen)
+ parse_extra_args(line + len + 1, pktlen - len - 1);
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(daemon_service[i]);
@@ -578,9 +569,7 @@ static int execute(struct sockaddr *addr)
* Note: The directory here is probably context sensitive,
* and might depend on the actual service being performed.
*/
- interp_set_entry(interp_table,
- INTERP_SLOT_DIR, line + namelen + 5);
- return run_service(interp_table, s);
+ return run_service(line + namelen + 5, s);
}
}
diff --git a/environment.c b/environment.c
index bb96ac0a7..e278bce0e 100644
--- a/environment.c
+++ b/environment.c
@@ -43,6 +43,9 @@ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
+/* Parallel index stat data preload? */
+int core_preload_index = 0;
+
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
static char *work_tree;
diff --git a/git-pull.sh b/git-pull.sh
index 664fe3441..1cac898a2 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -16,13 +16,17 @@ cd_to_toplevel
test -z "$(git ls-files -u)" ||
die "You are in the middle of a conflicted merge."
-strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
+strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
rebase=$(git config --bool branch.$curr_branch_short.rebase)
while :
do
case "$1" in
+ -q|--quiet)
+ verbosity="$verbosity -q" ;;
+ -v|--verbose)
+ verbosity="$verbosity -v" ;;
-n|--no-stat|--no-summary)
no_stat=-n ;;
--stat|--summary)
@@ -121,7 +125,7 @@ test true = "$rebase" && {
"refs/remotes/$origin/$reflist" 2>/dev/null)"
}
orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
-git fetch --update-head-ok "$@" || exit 1
+git fetch $verbosity --update-head-ok "$@" || exit 1
curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
if test -n "$orig_head" && test "$curr_head" != "$orig_head"
@@ -182,4 +186,4 @@ test true = "$rebase" &&
exec git-rebase $strategy_args --onto $merge_head \
${oldremoteref:-$merge_head}
exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
- "$merge_name" HEAD $merge_head
+ "$merge_name" HEAD $merge_head $verbosity
diff --git a/git-send-email.perl b/git-send-email.perl
index 94ca5c89a..7508f8ff2 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,8 +22,12 @@ use Term::ReadLine;
use Getopt::Long;
use Data::Dumper;
use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
+use Error qw(:try);
use Git;
+Getopt::Long::Configure qw/ pass_through /;
+
package FakeTerm;
sub new {
my ($class, $reason) = @_;
@@ -38,7 +42,7 @@ package main;
sub usage {
print <<EOT;
-git send-email [options] <file | directory>...
+git send-email [options] <file | directory | rev-list options >
Composing:
--from <str> * Email From:
@@ -47,6 +51,7 @@ git send-email [options] <file | directory>...
--bcc <str> * Email Bcc:
--subject <str> * Email "Subject:"
--in-reply-to <str> * Email "In-Reply-To:"
+ --annotate * Review each patch that will be sent in an editor.
--compose * Open an editor for introduction.
Sending:
@@ -73,6 +78,8 @@ git send-email [options] <file | directory>...
--quiet * Output one line of info per email.
--dry-run * Don't actually send the emails.
--[no-]validate * Perform patch sanity checks. Default on.
+ --[no-]format-patch * understand any non optional arguments as
+ `git format-patch` ones.
EOT
exit(1);
@@ -124,12 +131,10 @@ my $auth;
sub unique_email_list(@);
sub cleanup_compose_files();
-# Constants (essentially)
-my $compose_filename = ".msg.$$";
-
# Variables we fill in automatically, or via prompting:
my (@to,@cc,@initial_cc,@bcclist,@xh,
- $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+ $initial_reply_to,$initial_subject,@files,
+ $author,$sender,$smtp_authpass,$annotate,$compose,$time);
my $envelope_sender;
@@ -149,6 +154,27 @@ if ($@) {
# Behavior modification variables
my ($quiet, $dry_run) = (0, 0);
+my $format_patch;
+my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
+
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+ if (defined($multiedit) && !$multiedit) {
+ map {
+ system('sh', '-c', $editor.' "$@"', $editor, $_);
+ if (($? & 127) || ($? >> 8)) {
+ die("the editor exited uncleanly, aborting everything");
+ }
+ } @_;
+ } else {
+ system('sh', '-c', $editor.' "$@"', $editor, @_);
+ if (($? & 127) || ($? >> 8)) {
+ die("the editor exited uncleanly, aborting everything");
+ }
+ }
+}
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
@@ -179,6 +205,7 @@ my %config_settings = (
"aliasesfile" => \@alias_files,
"suppresscc" => \@suppress_cc,
"envelopesender" => \$envelope_sender,
+ "multiedit" => \$multiedit,
);
# Handle Uncouth Termination
@@ -221,6 +248,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
"smtp-encryption=s" => \$smtp_encryption,
"identity=s" => \$identity,
+ "annotate" => \$annotate,
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,
@@ -231,6 +259,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"envelope-sender=s" => \$envelope_sender,
"thread!" => \$thread,
"validate!" => \$validate,
+ "format-patch!" => \$format_patch,
);
unless ($rc) {
@@ -345,10 +374,13 @@ my %parse_alias = (
# spaces delimit multiple addresses
$aliases{$1} = [ split(/\s+/, $2) ];
}}},
- pine => sub { my $fh = shift; while (<$fh>) {
- if (/^(\S+)\t.*\t(.*)$/) {
+ pine => sub { my $fh = shift; my $f='\t[^\t]*';
+ for (my $x = ''; defined($x); $x = $_) {
+ chomp $x;
+ $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
+ $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
$aliases{$1} = [ split(/\s*,\s*/, $2) ];
- }}},
+ }},
gnus => sub { my $fh = shift; while (<$fh>) {
if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
$aliases{$1} = [ $2 ];
@@ -365,23 +397,52 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
($sender) = expand_aliases($sender) if defined $sender;
+# returns 1 if the conflict must be solved using it as a format-patch argument
+sub check_file_rev_conflict($) {
+ my $f = shift;
+ try {
+ $repo->command('rev-parse', '--verify', '--quiet', $f);
+ if (defined($format_patch)) {
+ print "foo\n";
+ return $format_patch;
+ }
+ die(<<EOF);
+File '$f' exists but it could also be the range of commits
+to produce patches for. Please disambiguate by...
+
+ * Saying "./$f" if you mean a file; or
+ * Giving --format-patch option if you mean a range.
+EOF
+ } catch Git::Error::Command with {
+ return 0;
+ }
+}
+
# Now that all the defaults are set, process the rest of the command line
# arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
- if (-d $f) {
+my @rev_list_opts;
+while (my $f = pop @ARGV) {
+ if ($f eq "--") {
+ push @rev_list_opts, "--", @ARGV;
+ @ARGV = ();
+ } elsif (-d $f and !check_file_rev_conflict($f)) {
opendir(DH,$f)
or die "Failed to opendir $f: $!";
push @files, grep { -f $_ } map { +$f . "/" . $_ }
sort readdir(DH);
closedir(DH);
- } elsif (-f $f or -p $f) {
+ } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
push @files, $f;
} else {
- print STDERR "Skipping $f - not found.\n";
+ push @rev_list_opts, $f;
}
}
+if (@rev_list_opts) {
+ push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
@@ -400,6 +461,108 @@ if (@files) {
usage();
}
+sub get_patch_subject($) {
+ my $fn = shift;
+ open (my $fh, '<', $fn);
+ while (my $line = <$fh>) {
+ next unless ($line =~ /^Subject: (.*)$/);
+ close $fh;
+ return "GIT: $1\n";
+ }
+ close $fh;
+ die "No subject line in $fn ?";
+}
+
+if ($compose) {
+ # Note that this does not need to be secure, but we will make a small
+ # effort to have it be unique
+ open(C,">",$compose_filename)
+ or die "Failed to open for writing $compose_filename: $!";
+
+
+ my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+ my $tpl_subject = $initial_subject || '';
+ my $tpl_reply_to = $initial_reply_to || '';
+
+ print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+EOT
+ for my $f (@files) {
+ print C get_patch_subject($f);
+ }
+ close(C);
+
+ my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+ if ($annotate) {
+ do_edit($compose_filename, @files);
+ } else {
+ do_edit($compose_filename);
+ }
+
+ open(C2,">",$compose_filename . ".final")
+ or die "Failed to open $compose_filename.final : " . $!;
+
+ open(C,"<",$compose_filename)
+ or die "Failed to open $compose_filename : " . $!;
+
+ my $need_8bit_cte = file_has_nonascii($compose_filename);
+ my $in_body = 0;
+ my $summary_empty = 1;
+ while(<C>) {
+ next if m/^GIT: /;
+ if ($in_body) {
+ $summary_empty = 0 unless (/^\n$/);
+ } elsif (/^\n$/) {
+ $in_body = 1;
+ if ($need_8bit_cte) {
+ print C2 "MIME-Version: 1.0\n",
+ "Content-Type: text/plain; ",
+ "charset=utf-8\n",
+ "Content-Transfer-Encoding: 8bit\n";
+ }
+ } elsif (/^MIME-Version:/i) {
+ $need_8bit_cte = 0;
+ } elsif (/^Subject:\s*(.+)\s*$/i) {
+ $initial_subject = $1;
+ my $subject = $initial_subject;
+ $_ = "Subject: " .
+ ($subject =~ /[^[:ascii:]]/ ?
+ quote_rfc2047($subject) :
+ $subject) .
+ "\n";
+ } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+ $initial_reply_to = $1;
+ next;
+ } elsif (/^From:\s*(.+)\s*$/i) {
+ $sender = $1;
+ next;
+ } elsif (/^(?:To|Cc|Bcc):/i) {
+ print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+ next;
+ }
+ print C2 $_;
+ }
+ close(C);
+ close(C2);
+
+ if ($summary_empty) {
+ print "Summary email is empty, skipping it\n";
+ $compose = -1;
+ }
+} elsif ($annotate) {
+ do_edit(@files);
+}
+
my $prompting = 0;
if (!defined $sender) {
$sender = $repoauthor || $repocommitter || '';
@@ -444,17 +607,6 @@ sub expand_aliases {
@initial_cc = expand_aliases(@initial_cc);
@bcclist = expand_aliases(@bcclist);
-if (!defined $initial_subject && $compose) {
- while (1) {
- $_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
- last if defined $_;
- print "\n";
- }
-
- $initial_subject = $_;
- $prompting++;
-}
-
if ($thread && !defined $initial_reply_to && $prompting) {
while (1) {
$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@ -481,59 +633,6 @@ if (!defined $smtp_server) {
}
if ($compose) {
- # Note that this does not need to be secure, but we will make a small
- # effort to have it be unique
- open(C,">",$compose_filename)
- or die "Failed to open for writing $compose_filename: $!";
- print C "From $sender # This line is ignored.\n";
- printf C "Subject: %s\n\n", $initial_subject;
- printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
- close(C);
-
- my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
- system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
-
- open(C2,">",$compose_filename . ".final")
- or die "Failed to open $compose_filename.final : " . $!;
-
- open(C,"<",$compose_filename)
- or die "Failed to open $compose_filename : " . $!;
-
- my $need_8bit_cte = file_has_nonascii($compose_filename);
- my $in_body = 0;
- while(<C>) {
- next if m/^GIT: /;
- if (!$in_body && /^\n$/) {
- $in_body = 1;
- if ($need_8bit_cte) {
- print C2 "MIME-Version: 1.0\n",
- "Content-Type: text/plain; ",
- "charset=utf-8\n",
- "Content-Transfer-Encoding: 8bit\n";
- }
- }
- if (!$in_body && /^MIME-Version:/i) {
- $need_8bit_cte = 0;
- }
- if (!$in_body && /^Subject: ?(.*)/i) {
- my $subject = $1;
- $_ = "Subject: " .
- ($subject =~ /[^[:ascii:]]/ ?
- quote_rfc2047($subject) :
- $subject) .
- "\n";
- }
- print C2 $_;
- }
- close(C);
- close(C2);
-
while (1) {
$_ = $term->readline("Send this email? (y|n) ");
last if defined $_;
@@ -545,7 +644,9 @@ EOT
exit(0);
}
- @files = ($compose_filename . ".final", @files);
+ if ($compose > 0) {
+ @files = ($compose_filename . ".final", @files);
+ }
}
# Variables we set as part of the loop over files
diff --git a/interpolate.c b/interpolate.c
deleted file mode 100644
index 7f03bd99c..000000000
--- a/interpolate.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2006 Jon Loeliger
- */
-
-#include "git-compat-util.h"
-#include "interpolate.h"
-
-
-void interp_set_entry(struct interp *table, int slot, const char *value)
-{
- char *oldval = table[slot].value;
- char *newval = NULL;
-
- free(oldval);
-
- if (value)
- newval = xstrdup(value);
-
- table[slot].value = newval;
-}
-
-
-void interp_clear_table(struct interp *table, int ninterps)
-{
- int i;
-
- for (i = 0; i < ninterps; i++) {
- interp_set_entry(table, i, NULL);
- }
-}
-
-
-/*
- * Convert a NUL-terminated string in buffer orig
- * into the supplied buffer, result, whose length is reslen,
- * performing substitutions on %-named sub-strings from
- * the table, interps, with ninterps entries.
- *
- * Example interps:
- * {
- * { "%H", "example.org"},
- * { "%port", "123"},
- * { "%%", "%"},
- * }
- *
- * Returns the length of the substituted string (not including the final \0).
- * Like with snprintf, if the result is >= reslen, then it overflowed.
- */
-
-unsigned long interpolate(char *result, unsigned long reslen,
- const char *orig,
- const struct interp *interps, int ninterps)
-{
- const char *src = orig;
- char *dest = result;
- unsigned long newlen = 0;
- const char *name, *value;
- unsigned long namelen, valuelen;
- int i;
- char c;
-
- while ((c = *src)) {
- if (c == '%') {
- /* Try to match an interpolation string. */
- for (i = 0; i < ninterps; i++) {
- name = interps[i].name;
- namelen = strlen(name);
- if (strncmp(src, name, namelen) == 0)
- break;
- }
-
- /* Check for valid interpolation. */
- if (i < ninterps) {
- value = interps[i].value;
- if (!value) {
- src += namelen;
- continue;
- }
-
- valuelen = strlen(value);
- if (newlen + valuelen < reslen) {
- /* Substitute. */
- memcpy(dest, value, valuelen);
- dest += valuelen;
- }
- newlen += valuelen;
- src += namelen;
- continue;
- }
- }
- /* Straight copy one non-interpolation character. */
- if (newlen + 1 < reslen)
- *dest++ = *src;
- src++;
- newlen++;
- }
-
- /* XXX: the previous loop always keep room for the ending NUL,
- we just need to check if there was room for a NUL in the first place */
- if (reslen > 0)
- *dest = '\0';
- return newlen;
-}
diff --git a/interpolate.h b/interpolate.h
deleted file mode 100644
index 77407e67d..000000000
--- a/interpolate.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2006 Jon Loeliger
- */
-
-#ifndef INTERPOLATE_H
-#define INTERPOLATE_H
-
-/*
- * Convert a NUL-terminated string in buffer orig,
- * performing substitutions on %-named sub-strings from
- * the interpretation table.
- */
-
-struct interp {
- const char *name;
- char *value;
-};
-
-extern void interp_set_entry(struct interp *table, int slot, const char *value);
-extern void interp_clear_table(struct interp *table, int ninterps);
-
-extern unsigned long interpolate(char *result, unsigned long reslen,
- const char *orig,
- const struct interp *interps, int ninterps);
-
-#endif /* INTERPOLATE_H */
diff --git a/ll-merge.c b/ll-merge.c
index 4a716146f..fa2ca5250 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -8,7 +8,6 @@
#include "attr.h"
#include "xdiff-interface.h"
#include "run-command.h"
-#include "interpolate.h"
#include "ll-merge.h"
struct ll_merge_driver;
@@ -169,11 +168,12 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
int virtual_ancestor)
{
char temp[3][50];
- char cmdbuf[2048];
- struct interp table[] = {
- { "%O" },
- { "%A" },
- { "%B" },
+ struct strbuf cmd = STRBUF_INIT;
+ struct strbuf_expand_dict_entry dict[] = {
+ { "O", temp[0] },
+ { "A", temp[1] },
+ { "B", temp[2] },
+ { NULL }
};
struct child_process child;
const char *args[20];
@@ -189,17 +189,13 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
create_temp(src1, temp[1]);
create_temp(src2, temp[2]);
- interp_set_entry(table, 0, temp[0]);
- interp_set_entry(table, 1, temp[1]);
- interp_set_entry(table, 2, temp[2]);
-
- interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+ strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
memset(&child, 0, sizeof(child));
child.argv = args;
args[0] = "sh";
args[1] = "-c";
- args[2] = cmdbuf;
+ args[2] = cmd.buf;
args[3] = NULL;
status = run_command(&child);
@@ -224,6 +220,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
bad:
for (i = 0; i < 3; i++)
unlink(temp[i]);
+ strbuf_release(&cmd);
return status;
}
diff --git a/merge-recursive.c b/merge-recursive.c
index 7472d3ecc..0e988f2a0 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -16,7 +16,6 @@
#include "string-list.h"
#include "xdiff-interface.h"
#include "ll-merge.h"
-#include "interpolate.h"
#include "attr.h"
#include "merge-recursive.h"
#include "dir.h"
diff --git a/parse-options.c b/parse-options.c
index fd08bb425..9eb55cc8b 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -484,6 +484,28 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
return 0;
}
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
+ int unset)
+{
+ int *target = opt->value;
+
+ if (unset)
+ /* --no-quiet, --no-verbose */
+ *target = 0;
+ else if (opt->short_name == 'v') {
+ if (*target >= 0)
+ (*target)++;
+ else
+ *target = 1;
+ } else {
+ if (*target <= 0)
+ (*target)--;
+ else
+ *target = -1;
+ }
+ return 0;
+}
+
/*
* This should really be OPTION_FILENAME type as a part of
* parse_options that take prefix to do this while parsing.
diff --git a/parse-options.h b/parse-options.h
index 5199950c0..034162ec6 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -150,9 +150,15 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx);
/*----- some often used options -----*/
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
+#define OPT__VERBOSITY(var) \
+ { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
+ { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run")
#define OPT__ABBREV(var) \
{ OPTION_CALLBACK, 0, "abbrev", (var), "n", \
diff --git a/preload-index.c b/preload-index.c
new file mode 100644
index 000000000..a6855837a
--- /dev/null
+++ b/preload-index.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Linus Torvalds
+ */
+#include "cache.h"
+#include <pthread.h>
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to 20 threads, and we want
+ * to have at least 500 lstat's per thread for it to
+ * be worth starting a thread.
+ */
+#define MAX_PARALLEL (20)
+#define THREAD_COST (500)
+
+struct thread_data {
+ pthread_t pthread;
+ struct index_state *index;
+ const char **pathspec;
+ int offset, nr;
+};
+
+static void *preload_thread(void *_data)
+{
+ int nr;
+ struct thread_data *p = _data;
+ struct index_state *index = p->index;
+ struct cache_entry **cep = index->cache + p->offset;
+
+ nr = p->nr;
+ if (nr + p->offset > index->cache_nr)
+ nr = index->cache_nr - p->offset;
+
+ do {
+ struct cache_entry *ce = *cep++;
+ struct stat st;
+
+ if (ce_stage(ce))
+ continue;
+ if (ce_uptodate(ce))
+ continue;
+ if (!ce_path_match(ce, p->pathspec))
+ continue;
+ if (lstat(ce->name, &st))
+ continue;
+ if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
+ continue;
+ ce_mark_uptodate(ce);
+ } while (--nr > 0);
+ return NULL;
+}
+
+static void preload_index(struct index_state *index, const char **pathspec)
+{
+ int threads, i, work, offset;
+ struct thread_data data[MAX_PARALLEL];
+
+ if (!core_preload_index)
+ return;
+
+ threads = index->cache_nr / THREAD_COST;
+ if (threads < 2)
+ return;
+ if (threads > MAX_PARALLEL)
+ threads = MAX_PARALLEL;
+ offset = 0;
+ work = (index->cache_nr + threads - 1) / threads;
+ for (i = 0; i < threads; i++) {
+ struct thread_data *p = data+i;
+ p->index = index;
+ p->pathspec = pathspec;
+ p->offset = offset;
+ p->nr = work;
+ offset += work;
+ if (pthread_create(&p->pthread, NULL, preload_thread, p))
+ die("unable to create threaded lstat");
+ }
+ for (i = 0; i < threads; i++) {
+ struct thread_data *p = data+i;
+ if (pthread_join(p->pthread, NULL))
+ die("unable to join threaded lstat");
+ }
+}
+
+int read_index_preload(struct index_state *index, const char **pathspec)
+{
+ int retval = read_index(index);
+
+ preload_index(index, pathspec);
+ return retval;
+}
diff --git a/sha1_file.c b/sha1_file.c
index 75a748a64..6c0e251e0 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1749,6 +1749,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
error("bad packed object CRC for %s",
sha1_to_hex(sha1));
mark_bad_packed_object(p, sha1);
+ unuse_pack(&w_curs);
return NULL;
}
}
@@ -2288,7 +2289,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
memcpy(buffer, filename, dirlen);
strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
fd = mkstemp(buffer);
- if (fd < 0 && dirlen) {
+ if (fd < 0 && dirlen && errno == ENOENT) {
/* Make sure the directory exists */
memcpy(buffer, filename, dirlen);
buffer[dirlen-1] = 0;
@@ -2314,7 +2315,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
filename = sha1_file_name(sha1);
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
if (fd < 0) {
- if (errno == EPERM)
+ if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
else
return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
diff --git a/strbuf.c b/strbuf.c
index 720737d85..13be67e4d 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -237,6 +237,22 @@ void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
}
}
+size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
+ void *context)
+{
+ struct strbuf_expand_dict_entry *e = context;
+ size_t len;
+
+ for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
+ if (!strncmp(placeholder, e->placeholder, len)) {
+ if (e->value)
+ strbuf_addstr(sb, e->value);
+ return len;
+ }
+ }
+ return 0;
+}
+
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
{
size_t res;
diff --git a/strbuf.h b/strbuf.h
index eba7ba423..b1670d994 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -111,6 +111,11 @@ extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
+struct strbuf_expand_dict_entry {
+ const char *placeholder;
+ const char *value;
+};
+extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
__attribute__((format(printf,2,3)))
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index dfe3fbc74..caea292f1 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -43,6 +43,13 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
'
+test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
+
+ GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
test_expect_success 'diff attribute' '
git config diff.parrot.command echo &&
@@ -68,6 +75,13 @@ test_expect_success 'diff attribute should apply only to diff' '
'
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+ git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
test_expect_success 'diff attribute' '
git config --unset diff.parrot.command &&
@@ -94,6 +108,13 @@ test_expect_success 'diff attribute should apply only to diff' '
'
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+ git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
test_expect_success 'no diff with -diff' '
echo >.gitattributes "file -diff" &&
git diff | grep Binary
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 03ba26a0d..0b76e7c97 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -21,7 +21,7 @@ EOF
cat >hexdump <<'EOF'
#!/bin/sh
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
EOF
chmod +x hexdump
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
new file mode 100755
index 000000000..83e2e8ab8
--- /dev/null
+++ b/t/t5521-pull-options.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='pull options'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file &&
+ git commit -m one)
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q' '
+ mkdir clonedq &&
+ cd clonedq &&
+ git pull -q "$D/parent" >out 2>err &&
+ test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull' '
+ mkdir cloned &&
+ cd cloned &&
+ git pull "$D/parent" >out 2>err &&
+ test -s out
+'
+cd "$D"
+
+test_expect_success 'git pull -v' '
+ mkdir clonedv &&
+ cd clonedv &&
+ git pull -v "$D/parent" >out 2>err &&
+ test -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -v -q' '
+ mkdir clonedvq &&
+ cd clonedvq &&
+ git pull -v -q "$D/parent" >out 2>err &&
+ test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q -v' '
+ mkdir clonedqv &&
+ cd clonedqv &&
+ git pull -q -v "$D/parent" >out 2>err &&
+ test -s out
+'
+
+test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 561ae7d0a..617e97d96 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -292,4 +292,12 @@ test_expect_success '--compose adds MIME for utf8 subject' '
grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
'
+test_expect_success 'detects ambiguous reference/file conflict' '
+ echo master > master &&
+ git add master &&
+ git commit -m"add master" &&
+ test_must_fail git send-email --dry-run master 2>errors &&
+ grep disambiguate errors
+'
+
test_done
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 2848e46e3..938b7fe4b 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -16,7 +16,7 @@ compare_git_head_with () {
compare_svn_head_with () {
LC_ALL=en_US.UTF-8 svn log --limit 1 `git svn info --url` | \
- sed -e 1,3d -e "/^-\+\$/d" >current &&
+ sed -e 1,3d -e "/^-\{1,\}\$/d" >current &&
test_cmp current "$1"
}
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
index 6ddd7c19f..205743546 100755
--- a/t/t9301-fast-export.sh
+++ b/t/t9301-fast-export.sh
@@ -231,4 +231,12 @@ test_expect_success 'fast-export -C -C | fast-import' '
'
+test_expect_success 'fast-export | fast-import when master is tagged' '
+
+ git tag -m msg last &&
+ git fast-export -C -C --signed-tags=strip --all > output &&
+ test $(grep -c "^tag " output) = 3
+
+'
+
test_done
diff --git a/xdiff-interface.c b/xdiff-interface.c
index e8ef46d10..d782f06d9 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -254,16 +254,6 @@ static long ff_regexp(const char *line, long len,
line_buffer = xstrndup(line, len); /* make NUL terminated */
- /* Exclude terminating newline (and cr) from matching */
- if (len > 0 && line[len-1] == '\n') {
- if (len > 1 && line[len-2] == '\r')
- len -= 2;
- else
- len--;
- }
-
- line_buffer = xstrndup(line, len); /* make NUL terminated */
-
for (i = 0; i < regs->nr; i++) {
struct ff_reg *reg = regs->array + i;
if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
@@ -338,4 +328,3 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
}
return git_default_config(var, value, cb);
}
-