summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes-1.5.3.5.txt10
-rwxr-xr-xDocumentation/cmd-list.perl1
-rw-r--r--Documentation/core-tutorial.txt8
-rw-r--r--Documentation/diff-format.txt19
-rw-r--r--Documentation/git-bisect.txt29
-rw-r--r--Documentation/git-clone.txt6
-rw-r--r--Documentation/git-cvsexportcommit.txt2
-rw-r--r--Documentation/git-diff.txt3
-rw-r--r--Documentation/git-filter-branch.txt2
-rw-r--r--Documentation/git-format-patch.txt2
-rw-r--r--Documentation/git-merge.txt11
-rw-r--r--Documentation/git-mergetool.txt8
-rw-r--r--Documentation/git-rev-list.txt16
-rw-r--r--Documentation/git.txt1
-rw-r--r--Makefile8
-rw-r--r--builtin-add.c20
-rw-r--r--builtin-archive.c8
-rw-r--r--builtin-fetch-pack.c101
-rw-r--r--builtin-pack-objects.c65
-rw-r--r--builtin-prune-packed.c14
-rw-r--r--builtin-rerere.c31
-rw-r--r--builtin-rev-list.c100
-rw-r--r--builtin-revert.c2
-rw-r--r--builtin-unpack-objects.c10
-rw-r--r--builtin.h1
-rw-r--r--cache.h4
-rw-r--r--color.c18
-rw-r--r--color.h4
-rw-r--r--commit.h5
-rw-r--r--connect.c128
-rwxr-xr-xcontrib/completion/git-completion.bash1
-rwxr-xr-xcontrib/examples/git-svnimport.perl (renamed from git-svnimport.perl)0
-rw-r--r--contrib/examples/git-svnimport.txt (renamed from Documentation/git-svnimport.txt)0
-rw-r--r--convert.c88
-rw-r--r--csum-file.c31
-rw-r--r--csum-file.h4
-rw-r--r--diff.c93
-rw-r--r--diffcore-rename.c344
-rw-r--r--diffcore.h4
-rw-r--r--exec_cmd.c132
-rw-r--r--exec_cmd.h3
-rwxr-xr-xgit-bisect.sh252
-rwxr-xr-xgit-cvsexportcommit.perl13
-rwxr-xr-xgit-mergetool.sh107
-rwxr-xr-xgit-rebase--interactive.sh7
-rwxr-xr-xgit-rebase.sh26
-rwxr-xr-xgit-repack.sh3
-rwxr-xr-xgit-sh-setup.sh2
-rw-r--r--git.c52
-rw-r--r--gitweb/INSTALL2
-rwxr-xr-xgitweb/gitweb.perl189
-rw-r--r--hash.c110
-rw-r--r--hash.h43
-rw-r--r--help.c185
-rw-r--r--index-pack.c42
-rw-r--r--log-tree.c2
-rw-r--r--log-tree.h1
-rw-r--r--pack-write.c3
-rw-r--r--pack.h2
-rw-r--r--peek-remote.c8
-rw-r--r--progress.c186
-rw-r--r--progress.h21
-rw-r--r--run-command.c77
-rw-r--r--run-command.h23
-rw-r--r--send-pack.c58
-rw-r--r--shell.c8
-rw-r--r--sideband.c19
-rwxr-xr-xt/t0021-conversion.sh7
-rwxr-xr-xt/t5516-fetch-push.sh28
-rwxr-xr-xt/t6030-bisect-porcelain.sh118
-rw-r--r--t/test-lib.sh1
-rw-r--r--transport.c11
-rw-r--r--unpack-trees.c14
-rw-r--r--upload-pack.c199
-rw-r--r--wt-status.c88
-rw-r--r--wt-status.h4
77 files changed, 2016 insertions, 1233 deletions
diff --git a/.gitignore b/.gitignore
index 249b451..c8c13f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,7 +128,6 @@ git-status
git-stripspace
git-submodule
git-svn
-git-svnimport
git-symbolic-ref
git-tag
git-tar-tree
diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt
index e28d92f..4e46d2c 100644
--- a/Documentation/RelNotes-1.5.3.5.txt
+++ b/Documentation/RelNotes-1.5.3.5.txt
@@ -87,8 +87,8 @@ Fixes since v1.5.3.4
* A few workarounds to squelch false warnings from recent gcc have
been added.
---
-exec >/var/tmp/1
-O=v1.5.3.4-55-gf120ae2
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
+ * "git-send-pack $remote frotz" segfaulted when there is nothing
+ named 'frotz' on the local end.
+
+ * "git-rebase -interactive" did not handle its "--strategy" option
+ properly.
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 1061fd8..8d21d42 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -185,7 +185,6 @@ git-status mainporcelain
git-stripspace purehelpers
git-submodule mainporcelain
git-svn foreignscminterface
-git-svnimport foreignscminterface
git-symbolic-ref plumbingmanipulators
git-tag mainporcelain
git-tar-tree plumbinginterrogators
diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index d8e78ac..5df97a1 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -878,7 +878,7 @@ script called `git merge`, which wants to know which branches you want
to resolve and what the merge is all about:
------------
-$ git merge "Merge work in mybranch" HEAD mybranch
+$ git merge -m "Merge work in mybranch" mybranch
------------
where the first argument is going to be used as the commit message if
@@ -965,7 +965,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
------------
$ git checkout mybranch
-$ git merge "Merge upstream changes." HEAD master
+$ git merge -m "Merge upstream changes." master
------------
This outputs something like this (the actual commit object names
@@ -1607,8 +1607,8 @@ in both of them. You could merge in 'diff-fix' first and then
'commit-fix' next, like this:
------------
-$ git merge 'Merge fix in diff-fix' master diff-fix
-$ git merge 'Merge fix in commit-fix' master commit-fix
+$ git merge -m 'Merge fix in diff-fix' diff-fix
+$ git merge -m 'Merge fix in commit-fix' commit-fix
------------
Which would result in:
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index 0015032..9709c35 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -1,5 +1,5 @@
-The output format from "git-diff-index", "git-diff-tree" and
-"git-diff-files" are very similar.
+The output format from "git-diff-index", "git-diff-tree",
+"git-diff-files" and "git diff --raw" are very similar.
These commands all compare two sets of things; what is
compared differs:
@@ -62,7 +62,8 @@ respectively.
diff format for merges
----------------------
-"git-diff-tree" and "git-diff-files" can take '-c' or '--cc' option
+"git-diff-tree", "git-diff-files" and "git-diff --raw"
+can take '-c' or '--cc' option
to generate diff output also for merge commits. The output differs
from the format described above in the following way:
@@ -86,10 +87,10 @@ Generating patches with -p
--------------------------
When "git-diff-index", "git-diff-tree", or "git-diff-files" are run
-with a '-p' option, they do not produce the output described above;
-instead they produce a patch file. You can customize the creation
-of such patches via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS
-environment variables.
+with a '-p' option, or "git diff" without the '--raw' option, they
+do not produce the output described above; instead they produce a
+patch file. You can customize the creation of such patches via the
+GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
What the -p option produces is slightly different from the traditional
diff format.
@@ -137,8 +138,8 @@ file made it into the new one.
combined diff format
--------------------
-git-diff-tree and git-diff-files can take '-c' or '--cc' option
-to produce 'combined diff', which looks like this:
+"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
+'--cc' option to produce 'combined diff', which looks like this:
------------
diff --combined describe.c
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 1072fb8..4795349 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -16,8 +16,9 @@ The command takes various subcommands, and different options depending
on the subcommand:
git bisect start [<bad> [<good>...]] [--] [<paths>...]
- git bisect bad <rev>
- git bisect good <rev>
+ git bisect bad [<rev>]
+ git bisect good [<rev>...]
+ git bisect skip [<rev>...]
git bisect reset [<branch>]
git bisect visualize
git bisect replay <logfile>
@@ -134,6 +135,20 @@ $ git reset --hard HEAD~3 # try 3 revs before what
Then compile and test the one you chose to try. After that, tell
bisect what the result was as usual.
+Bisect skip
+~~~~~~~~~~~~
+
+Instead of choosing by yourself a nearby commit, you may just want git
+to do it for you using:
+
+------------
+$ git bisect skip # Current version cannot be tested
+------------
+
+But computing the commit to test may be slower afterwards and git may
+eventually not be able to tell the first bad among a bad and one or
+more "skip"ped commits.
+
Cutting down bisection by giving more parameters to bisect start
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -167,14 +182,18 @@ $ git bisect run my_script
------------
Note that the "run" script (`my_script` in the above example) should
-exit with code 0 in case the current source code is good and with a
-code between 1 and 127 (included) in case the current source code is
-bad.
+exit with code 0 in case the current source code is good. Exit with a
+code between 1 and 127 (inclusive), except 125, if the current
+source code is bad.
Any other exit code will abort the automatic bisect process. (A
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
the value is chopped with "& 0377".)
+The special exit code 125 should be used when the current source code
+cannot be tested. If the "run" script exits with this code, the current
+revision will be skipped, see `git bisect skip` above.
+
You may often find that during bisect you want to have near-constant
tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
"revision that does not have this commit needs this patch applied to
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 253f4f0..cca14d6 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -111,11 +111,11 @@ OPTIONS
--depth <depth>::
Create a 'shallow' clone with a history truncated to the
- specified number of revs. A shallow repository has
+ specified number of revisions. A shallow repository has a
number of limitations (you cannot clone or fetch from
it, nor push from nor into it), but is adequate if you
- want to only look at near the tip of a large project
- with a long history, and would want to send in a fixes
+ are only interested in the recent history of a large project
+ with a long history, and would want to send in fixes
as patches.
<repository>::
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index 4c8d1e6..c3922f9 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -73,7 +73,7 @@ Merge one patch into CVS::
$ export GIT_DIR=~/project/.git
$ cd ~/project_cvs_checkout
$ git-cvsexportcommit -v <commit-sha1>
-$ cvs commit -F .mgs <files>
+$ cvs commit -F .msg <files>
------------
Merge pending patches into CVS automatically -- only if you really know what you are doing::
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index ce0f502..11c4216 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -82,6 +82,9 @@ include::diff-options.txt[]
the diff to the named paths (you can give directory
names and get diff for all files under them).
+Output format
+-------------
+include::diff-format.txt[]
EXAMPLES
--------
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index ba9b4fb..385ecc9 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -219,7 +219,7 @@ git filter-branch --commit-filter '
fi' HEAD
------------------------------------------------------------------------------
-The function 'skip_commits' is defined as follows:
+The function 'skip_commit' is defined as follows:
--------------------------
skip_commit()
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index c9857a2..f0617ef 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -168,7 +168,7 @@ git-format-patch origin::
is created in the current directory.
git-format-patch \--root origin::
- Extract all commits which that leads to 'origin' since the
+ Extract all commits that lead to 'origin' since the
inception of the project.
git-format-patch -M -B origin::
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index bca4212..eabd7ef 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -11,26 +11,27 @@ SYNOPSIS
[verse]
'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
[-m <msg>] <remote> <remote>...
+'git-merge' <msg> HEAD <remote>...
DESCRIPTION
-----------
This is the top-level interface to the merge machinery
which drives multiple merge strategy scripts.
+The second syntax (<msg> `HEAD` <remote>) is supported for
+historical reasons. Do not use it from the command line or in
+new scripts. It is the same as `git merge -m <msg> <remote>`.
+
OPTIONS
-------
include::merge-options.txt[]
-<msg>::
+-m <msg>::
The commit message to be used for the merge commit (in case
it is created). The `git-fmt-merge-msg` script can be used
to give a good default for automated `git-merge` invocations.
-<head>::
- Our branch head commit. This has to be `HEAD`, so new
- syntax does not require it
-
<remote>::
Other branch head merged into our branch. You need at
least one <remote>. Specifying more than one <remote>
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 6c32c6d..a26c260 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -25,12 +25,18 @@ OPTIONS
-t or --tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
- kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, and opendiff
+ kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the
configuration variable merge.tool is not set, 'git mergetool'
will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable mergetool.<tool>.path. For example, you
+can configure the absolute path to kdiff3 by setting
+mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
+is available in PATH.
Author
------
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 7cd0e89..4852804 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -34,6 +34,7 @@ SYNOPSIS
[ \--pretty | \--header ]
[ \--bisect ]
[ \--bisect-vars ]
+ [ \--bisect-all ]
[ \--merge ]
[ \--reverse ]
[ \--walk-reflogs ]
@@ -354,6 +355,21 @@ the expected number of commits to be tested if `bisect_rev`
turns out to be bad to `bisect_bad`, and the number of commits
we are bisecting right now to `bisect_all`.
+--bisect-all::
+
+This outputs all the commit objects between the included and excluded
+commits, ordered by their distance to the included and excluded
+commits. The farthest from them is displayed first. (This is the only
+one displayed by `--bisect`.)
+
+This is useful because it makes it easy to choose a good commit to
+test when you want to avoid to test some of them for some reason (they
+may not compile for example).
+
+This option can be used along with `--bisect-vars`, in this case,
+after all the sorted commit objects, there will be the same text as if
+`--bisect-vars` had been used alone.
+
--
Commit Ordering
diff --git a/Documentation/git.txt b/Documentation/git.txt
index c4d87ac..6db7ae1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -46,6 +46,7 @@ Documentation for older releases are available here:
* link:v1.5.3/git.html[documentation for release 1.5.3]
* release notes for
+ link:RelNotes-1.5.3.5.txt[1.5.3.5],
link:RelNotes-1.5.3.4.txt[1.5.3.4],
link:RelNotes-1.5.3.3.txt[1.5.3.3],
link:RelNotes-1.5.3.2.txt[1.5.3.2],
diff --git a/Makefile b/Makefile
index 3c9af55..042f79e 100644
--- a/Makefile
+++ b/Makefile
@@ -225,8 +225,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-add--interactive.perl \
git-archimport.perl git-cvsimport.perl git-relink.perl \
- git-cvsserver.perl git-remote.perl \
- git-svnimport.perl git-cvsexportcommit.perl \
+ git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -290,7 +289,7 @@ LIB_H = \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
- mailmap.h remote.h parse-options.h transport.h
+ mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -300,7 +299,7 @@ DIFF_OBJS = \
LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \
- interpolate.o \
+ interpolate.o hash.o \
lockfile.o \
patch-ids.o \
object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
@@ -917,7 +916,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
-$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
diff --git a/builtin-add.c b/builtin-add.c
index f61681c..45b14e8 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -111,7 +111,7 @@ static void update_callback(struct diff_queue_struct *q,
}
}
-static void update(int verbose, const char *prefix, const char **files)
+void add_files_to_cache(int verbose, const char *prefix, const char **files)
{
struct rev_info rev;
init_revisions(&rev, prefix);
@@ -120,8 +120,6 @@ static void update(int verbose, const char *prefix, const char **files)
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &verbose;
- if (read_cache() < 0)
- die("index file corrupt");
run_diff_files(&rev, 0);
}
@@ -155,6 +153,13 @@ static int git_add_config(const char *var, const char *value)
return git_default_config(var, value);
}
+int interactive_add(void)
+{
+ const char *argv[2] = { "add--interactive", NULL };
+
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
static struct lock_file lock_file;
static const char ignore_error[] =
@@ -183,12 +188,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, builtin_add_options,
builtin_add_usage, 0);
if (add_interactive) {
- const char *args[] = { "add--interactive", NULL };
-
if (add_interactive != 1 || orig_argc != 2)
die("add --interactive does not take any parameters");
- execv_git_cmd(args);
- exit(1);
+ exit(interactive_add());
}
git_config(git_add_config);
@@ -196,7 +198,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
newfd = hold_locked_index(&lock_file, 1);
if (take_worktree_changes) {
- update(verbose, prefix, argv);
+ if (read_cache() < 0)
+ die("index file corrupt");
+ add_files_to_cache(verbose, prefix, argv);
goto finish;
}
diff --git a/builtin-archive.c b/builtin-archive.c
index 6f29c2f..14a1b30 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -30,7 +30,7 @@ static int run_remote_archiver(const char *remote, int argc,
{
char *url, buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv;
- pid_t pid;
+ struct child_process *conn;
const char *exec = "git-upload-archive";
int exec_at = 0;
@@ -46,9 +46,7 @@ static int run_remote_archiver(const char *remote, int argc,
}
url = xstrdup(remote);
- pid = git_connect(fd, url, exec, 0);
- if (pid < 0)
- return pid;
+ conn = git_connect(fd, url, exec, 0);
for (i = 1; i < argc; i++) {
if (i == exec_at)
@@ -76,7 +74,7 @@ static int run_remote_archiver(const char *remote, int argc,
rv = recv_sideband("archive", fd[0], 1, 2);
close(fd[0]);
close(fd[1]);
- rv |= finish_connect(pid);
+ rv |= finish_connect(conn);
return !!rv;
}
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 8753840..862652b 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -7,6 +7,7 @@
#include "pack.h"
#include "sideband.h"
#include "fetch-pack.h"
+#include "run-command.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -457,53 +458,49 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
return retval;
}
-static pid_t setup_sideband(int fd[2], int xd[2])
+static int sideband_demux(int fd, void *data)
{
- pid_t side_pid;
+ int *xd = data;
+ close(xd[1]);
+ return recv_sideband("fetch-pack", xd[0], fd, 2);
+}
+
+static void setup_sideband(int fd[2], int xd[2], struct async *demux)
+{
if (!use_sideband) {
fd[0] = xd[0];
fd[1] = xd[1];
- return 0;
+ return;
}
/* xd[] is talking with upload-pack; subprocess reads from
* xd[0], spits out band#2 to stderr, and feeds us band#1
- * through our fd[0].
+ * through demux->out.
*/
- if (pipe(fd) < 0)
- die("fetch-pack: unable to set up pipe");
- side_pid = fork();
- if (side_pid < 0)
+ demux->proc = sideband_demux;
+ demux->data = xd;
+ if (start_async(demux))
die("fetch-pack: unable to fork off sideband demultiplexer");
- if (!side_pid) {
- /* subprocess */
- close(fd[0]);
- if (xd[0] != xd[1])
- close(xd[1]);
- if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
- exit(1);
- exit(0);
- }
close(xd[0]);
- close(fd[1]);
+ fd[0] = demux->out;
fd[1] = xd[1];
- return side_pid;
}
static int get_pack(int xd[2], char **pack_lockfile)
{
- int status;
- pid_t pid, side_pid;
+ struct async demux;
int fd[2];
const char *argv[20];
char keep_arg[256];
char hdr_arg[256];
const char **av;
int do_keep = args.keep_pack;
- int keep_pipe[2];
+ struct child_process cmd;
- side_pid = setup_sideband(fd, xd);
+ setup_sideband(fd, xd, &demux);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.argv = argv;
av = argv;
*hdr_arg = 0;
if (!args.keep_pack && unpack_limit) {
@@ -520,8 +517,8 @@ static int get_pack(int xd[2], char **pack_lockfile)
}
if (do_keep) {
- if (pack_lockfile && pipe(keep_pipe))
- die("fetch-pack: pipe setup failure: %s", strerror(errno));
+ if (pack_lockfile)
+ cmd.out = -1;
*av++ = "index-pack";
*av++ = "--stdin";
if (!args.quiet && !args.no_progress)
@@ -545,43 +542,19 @@ static int get_pack(int xd[2], char **pack_lockfile)
*av++ = hdr_arg;
*av++ = NULL;
- pid = fork();
- if (pid < 0)
+ cmd.in = fd[0];
+ cmd.git_cmd = 1;
+ if (start_command(&cmd))
die("fetch-pack: unable to fork off %s", argv[0]);
- if (!pid) {
- dup2(fd[0], 0);
- if (do_keep && pack_lockfile) {
- dup2(keep_pipe[1], 1);
- close(keep_pipe[0]);
- close(keep_pipe[1]);
- }
- close(fd[0]);
- close(fd[1]);
- execv_git_cmd(argv);
- die("%s exec failed", argv[0]);
- }
- close(fd[0]);
close(fd[1]);
- if (do_keep && pack_lockfile) {
- close(keep_pipe[1]);
- *pack_lockfile = index_pack_lockfile(keep_pipe[0]);
- close(keep_pipe[0]);
- }
- while (waitpid(pid, &status, 0) < 0) {
- if (errno != EINTR)
- die("waiting for %s: %s", argv[0], strerror(errno));
- }
- if (WIFEXITED(status)) {
- int code = WEXITSTATUS(status);
- if (code)
- die("%s died with error code %d", argv[0], code);
- return 0;
- }
- if (WIFSIGNALED(status)) {
- int sig = WTERMSIG(status);
- die("%s died of signal %d", argv[0], sig);
- }
- die("%s died of unnatural causes %d", argv[0], status);
+ if (do_keep && pack_lockfile)
+ *pack_lockfile = index_pack_lockfile(cmd.out);
+
+ if (finish_command(&cmd))
+ die("%s failed", argv[0]);
+ if (use_sideband && finish_async(&demux))
+ die("error in sideband demultiplexer");
+ return 0;
}
static struct ref *do_fetch_pack(int fd[2],
@@ -763,7 +736,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
{
int i, ret;
int fd[2];
- pid_t pid;
+ struct child_process *conn;
struct ref *ref;
struct stat st;
@@ -774,16 +747,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
st.st_mtime = 0;
}
- pid = git_connect(fd, (char *)dest, args.uploadpack,
+ conn = git_connect(fd, (char *)dest, args.uploadpack,
args.verbose ? CONNECT_VERBOSE : 0);
- if (pid < 0)
- return NULL;
if (heads && nr_heads)
nr_heads = remove_duplicates(nr_heads, heads);
ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
close(fd[0]);
close(fd[1]);
- ret = finish_connect(pid);
+ ret = finish_connect(conn);
if (!ret && nr_heads) {
/* If the heads to pull were given, we should have
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 0be539e..25ec65d 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -65,8 +65,6 @@ static int no_reuse_delta, no_reuse_object, keep_unreachable;
static int local;
static int incremental;
static int allow_ofs_delta;
-static const char *pack_tmp_name, *idx_tmp_name;
-static char tmpname[PATH_MAX];
static const char *base_name;
static int progress = 1;
static int window = 10;
@@ -75,7 +73,7 @@ static int depth = 50;
static int delta_search_threads = 1;
static int pack_to_stdout;
static int num_preferred_base;
-static struct progress progress_state;
+static struct progress *progress_state;
static int pack_compression_level = Z_DEFAULT_COMPRESSION;
static int pack_compression_seen;
@@ -587,12 +585,6 @@ static off_t write_one(struct sha1file *f,
return offset + size;
}
-static int open_object_dir_tmp(const char *path)
-{
- snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path);
- return xmkstemp(tmpname);
-}
-
/* forward declaration for write_pack_file */
static int adjust_perm(const char *path, mode_t mode);
@@ -606,16 +598,21 @@ static void write_pack_file(void)
uint32_t nr_remaining = nr_result;
if (do_progress)
- start_progress(&progress_state, "Writing %u objects...", "", nr_result);
+ progress_state = start_progress("Writing objects", nr_result);
written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
do {
unsigned char sha1[20];
+ char *pack_tmp_name = NULL;
if (pack_to_stdout) {
- f = sha1fd(1, "<stdout>");
+ f = sha1fd_throughput(1, "<stdout>", progress_state);
} else {
- int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
+ char tmpname[PATH_MAX];
+ int fd;
+ snprintf(tmpname, sizeof(tmpname),
+ "%s/tmp_pack_XXXXXX", get_object_directory());
+ fd = xmkstemp(tmpname);
pack_tmp_name = xstrdup(tmpname);
f = sha1fd(fd, pack_tmp_name);
}
@@ -632,8 +629,7 @@ static void write_pack_file(void)
if (!offset_one)
break;
offset = offset_one;
- if (do_progress)
- display_progress(&progress_state, written);
+ display_progress(progress_state, written);
}
/*
@@ -643,19 +639,21 @@ static void write_pack_file(void)
if (pack_to_stdout || nr_written == nr_remaining) {
sha1close(f, sha1, 1);
} else {
- sha1close(f, sha1, 0);
- fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written);
- close(f->fd);
+ int fd = sha1close(f, NULL, 0);
+ fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written);
+ close(fd);
}
if (!pack_to_stdout) {
mode_t mode = umask(0);
+ char *idx_tmp_name, tmpname[PATH_MAX];
umask(mode);
mode = 0444 & ~mode;
idx_tmp_name = write_idx_file(NULL,
- (struct pack_idx_entry **) written_list, nr_written, sha1);
+ (struct pack_idx_entry **) written_list,
+ nr_written, sha1);
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
base_name, sha1_to_hex(sha1));
if (adjust_perm(pack_tmp_name, mode))
@@ -672,6 +670,8 @@ static void write_pack_file(void)
if (rename(idx_tmp_name, tmpname))
die("unable to rename temporary index file: %s",
strerror(errno));
+ free(idx_tmp_name);
+ free(pack_tmp_name);
puts(sha1_to_hex(sha1));
}
@@ -683,8 +683,7 @@ static void write_pack_file(void)
} while (nr_remaining && i < nr_objects);
free(written_list);
- if (do_progress)
- stop_progress(&progress_state);
+ stop_progress(&progress_state);
if (written != nr_result)
die("wrote %u objects while expecting %u", written, nr_result);
/*
@@ -852,8 +851,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
else
object_ix[-1 - ix] = nr_objects;
- if (progress)
- display_progress(&progress_state, nr_objects);
+ display_progress(progress_state, nr_objects);
if (name && no_try_delta(name))
entry->no_try_delta = 1;
@@ -1516,8 +1514,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size,
progress_lock();
(*processed)++;
- if (progress)
- display_progress(&progress_state, *processed);
+ display_progress(progress_state, *processed);
progress_unlock();
/*
@@ -1714,16 +1711,14 @@ static void prepare_pack(int window, int depth)
delta_list[n++] = entry;
}
- if (nr_deltas) {
+ if (nr_deltas && n > 1) {
unsigned nr_done = 0;
if (progress)
- start_progress(&progress_state,
- "Deltifying %u objects...", "",
- nr_deltas);
+ progress_state = start_progress("Compressing objects",
+ nr_deltas);
qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
- if (progress)
- stop_progress(&progress_state);
+ stop_progress(&progress_state);
if (nr_done != nr_deltas)
die("inconsistency with delta count");
}
@@ -2135,23 +2130,17 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
prepare_packed_git();
if (progress)
- start_progress(&progress_state, "Generating pack...",
- "Counting objects: ", 0);
+ progress_state = start_progress("Counting objects", 0);
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
- if (progress) {
- stop_progress(&progress_state);
- fprintf(stderr, "Done counting %u objects.\n", nr_objects);
- }
+ stop_progress(&progress_state);
if (non_empty && !nr_result)
return 0;
- if (progress && (nr_objects != nr_result))
- fprintf(stderr, "Result has %u objects.\n", nr_result);
if (nr_result)
prepare_pack(window, depth);
write_pack_file();
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index 9777300..23faf31 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "progress.h"
static const char prune_packed_usage[] =
"git-prune-packed [-n] [-q]";
@@ -7,6 +8,8 @@ static const char prune_packed_usage[] =
#define DRY_RUN 01
#define VERBOSE 02
+static struct progress *progress;
+
static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
{
struct dirent *de;
@@ -27,6 +30,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
printf("rm -f %s\n", pathname);
else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname);
+ display_progress(progress, i + 1);
}
pathname[len] = 0;
rmdir(pathname);
@@ -39,6 +43,10 @@ void prune_packed_objects(int opts)
const char *dir = get_object_directory();
int len = strlen(dir);
+ if (opts == VERBOSE)
+ progress = start_progress_delay("Removing duplicate objects",
+ 256, 95, 2);
+
if (len > PATH_MAX - 42)
die("impossible object directory");
memcpy(pathname, dir, len);
@@ -49,16 +57,12 @@ void prune_packed_objects(int opts)
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
- if (opts == VERBOSE && (d || i == 255))
- fprintf(stderr, "Removing unused objects %d%%...\015",
- ((i+1) * 100) / 256);
if (!d)
continue;
prune_dir(i, d, pathname, len + 3, opts);
closedir(d);
}
- if (opts == VERBOSE)
- fprintf(stderr, "\nDone.\n");
+ stop_progress(&progress);
}
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
diff --git a/builtin-rerere.c b/builtin-rerere.c
index b820674..7449323 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -389,18 +389,39 @@ static int is_rerere_enabled(void)
return 1;
}
-int cmd_rerere(int argc, const char **argv, const char *prefix)
+static int setup_rerere(struct path_list *merge_rr)
{
- struct path_list merge_rr = { NULL, 0, 0, 1 };
- int i, fd = -1;
+ int fd;
git_config(git_rerere_config);
if (!is_rerere_enabled())
- return 0;
+ return -1;
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
- read_rr(&merge_rr);
+ read_rr(merge_rr);
+ return fd;
+}
+
+int rerere(void)
+{
+ struct path_list merge_rr = { NULL, 0, 0, 1 };
+ int fd;
+
+ fd = setup_rerere(&merge_rr);
+ if (fd < 0)
+ return 0;
+ return do_plain_rerere(&merge_rr, fd);
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+ struct path_list merge_rr = { NULL, 0, 0, 1 };
+ int i, fd;
+
+ fd = setup_rerere(&merge_rr);
+ if (fd < 0)
+ return 0;
if (argc < 2)
return do_plain_rerere(&merge_rr, fd);
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 33726b8..4439332 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -9,6 +9,7 @@
#include "revision.h"
#include "list-objects.h"
#include "builtin.h"
+#include "log-tree.h"
/* bits #0-15 in revision.h */
@@ -38,7 +39,8 @@ static const char rev_list_usage[] =
" --left-right\n"
" special purpose:\n"
" --bisect\n"
-" --bisect-vars"
+" --bisect-vars\n"
+" --bisect-all"
;
static struct rev_info revs;
@@ -74,6 +76,7 @@ static void show_commit(struct commit *commit)
parents = parents->next;
}
}
+ show_decorations(commit);
if (revs.commit_format == CMIT_FMT_ONELINE)
putchar(' ');
else
@@ -278,6 +281,57 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
return best;
}
+struct commit_dist {
+ struct commit *commit;
+ int distance;
+};
+
+static int compare_commit_dist(const void *a_, const void *b_)
+{
+ struct commit_dist *a, *b;
+
+ a = (struct commit_dist *)a_;
+ b = (struct commit_dist *)b_;
+ if (a->distance != b->distance)
+ return b->distance - a->distance; /* desc sort */
+ return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+}
+
+static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
+{
+ struct commit_list *p;
+ struct commit_dist *array = xcalloc(nr, sizeof(*array));
+ int cnt, i;
+
+ for (p = list, cnt = 0; p; p = p->next) {
+ int distance;
+ unsigned flags = p->item->object.flags;
+
+ if (revs.prune_fn && !(flags & TREECHANGE))
+ continue;
+ distance = weight(p);
+ if (nr - distance < distance)
+ distance = nr - distance;
+ array[cnt].commit = p->item;
+ array[cnt].distance = distance;
+ cnt++;
+ }
+ qsort(array, cnt, sizeof(*array), compare_commit_dist);
+ for (p = list, i = 0; i < cnt; i++) {
+ struct name_decoration *r = xmalloc(sizeof(*r) + 100);
+ struct object *obj = &(array[i].commit->object);
+
+ sprintf(r->name, "dist=%d", array[i].distance);
+ r->next = add_decoration(&name_decoration, obj, r);
+ p->item = array[i].commit;
+ p = p->next;
+ }
+ if (p)
+ p->next = NULL;
+ free(array);
+ return list;
+}
+
/*
* zero or positive weight is the number of interesting commits it can
* reach, including itself. Especially, weight = 0 means it does not
@@ -292,7 +346,8 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
* or positive distance.
*/
static struct commit_list *do_find_bisection(struct commit_list *list,
- int nr, int *weights)
+ int nr, int *weights,
+ int find_all)
{
int n, counted;
struct commit_list *p;
@@ -351,7 +406,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
clear_distance(list);
/* Does it happen to be at exactly half-way? */
- if (halfway(p, nr))
+ if (!find_all && halfway(p, nr))
return p;
counted++;
}
@@ -389,19 +444,22 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
weight_set(p, weight(q));
/* Does it happen to be at exactly half-way? */
- if (halfway(p, nr))
+ if (!find_all && halfway(p, nr))
return p;
}
}
show_list("bisection 2 counted all", counted, nr, list);
- /* Then find the best one */
- return best_bisection(list, nr);
+ if (!find_all)
+ return best_bisection(list, nr);
+ else
+ return best_bisection_sorted(list, nr);
}
static struct commit_list *find_bisection(struct commit_list *list,
- int *reaches, int *all)
+ int *reaches, int *all,
+ int find_all)
{
int nr, on_list;
struct commit_list *p, *best, *next, *last;
@@ -434,14 +492,13 @@ static struct commit_list *find_bisection(struct commit_list *list,
weights = xcalloc(on_list, sizeof(*weights));
/* Do the real work of finding bisection commit. */
- best = do_find_bisection(list, nr, weights);
-
+ best = do_find_bisection(list, nr, weights, find_all);
if (best) {
- best->next = NULL;
+ if (!find_all)
+ best->next = NULL;
*reaches = weight(best);
}
free(weights);
-
return best;
}
@@ -468,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int i;
int read_from_stdin = 0;
int bisect_show_vars = 0;
+ int bisect_find_all = 0;
git_config(git_default_config);
init_revisions(&revs, prefix);
@@ -490,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
bisect_list = 1;
continue;
}
+ if (!strcmp(arg, "--bisect-all")) {
+ bisect_list = 1;
+ bisect_find_all = 1;
+ continue;
+ }
if (!strcmp(arg, "--bisect-vars")) {
bisect_list = 1;
bisect_show_vars = 1;
@@ -536,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list) {
int reaches = reaches, all = all;
- revs.commits = find_bisection(revs.commits, &reaches, &all);
+ revs.commits = find_bisection(revs.commits, &reaches, &all,
+ bisect_find_all);
if (bisect_show_vars) {
int cnt;
+ char hex[41];
if (!revs.commits)
return 1;
/*
@@ -550,15 +615,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
* A bisect set of size N has (N-1) commits further
* to test, as we already know one bad one.
*/
- cnt = all-reaches;
+ cnt = all - reaches;
if (cnt < reaches)
cnt = reaches;
+ strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
+
+ if (bisect_find_all) {
+ traverse_commit_list(&revs, show_commit, show_object);
+ printf("------\n");
+ }
+
printf("bisect_rev=%s\n"
"bisect_nr=%d\n"
"bisect_good=%d\n"
"bisect_bad=%d\n"
"bisect_all=%d\n",
- sha1_to_hex(revs.commits->item->object.sha1),
+ hex,
cnt - 1,
all - reaches - 1,
reaches - 1,
diff --git a/builtin-revert.c b/builtin-revert.c
index 2ea766b..a9347cf 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -341,7 +341,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
die ("Error wrapping up %s", defmsg);
fprintf(stderr, "Automatic %s failed. "
"After resolving the conflicts,\n"
- "mark the corrected paths with 'git-add <paths>'\n"
+ "mark the corrected paths with 'git add <paths>' "
"and commit the result.\n", me);
if (action == CHERRY_PICK) {
fprintf(stderr, "When commiting, use the option "
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index a6ff62f..1e51865 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -311,7 +311,7 @@ static void unpack_one(unsigned nr)
static void unpack_all(void)
{
int i;
- struct progress progress;
+ struct progress *progress = NULL;
struct pack_header *hdr = fill(sizeof(struct pack_header));
unsigned nr_objects = ntohl(hdr->hdr_entries);
@@ -322,15 +322,13 @@ static void unpack_all(void)
use(sizeof(struct pack_header));
if (!quiet)
- start_progress(&progress, "Unpacking %u objects...", "", nr_objects);
+ progress = start_progress("Unpacking objects", nr_objects);
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
- if (!quiet)
- display_progress(&progress, i + 1);
+ display_progress(progress, i + 1);
}
- if (!quiet)
- stop_progress(&progress);
+ stop_progress(&progress);
if (delta_list)
die("unresolved deltas left after unpacking");
diff --git a/builtin.h b/builtin.h
index 65cc0fb..9a6213a 100644
--- a/builtin.h
+++ b/builtin.h
@@ -6,6 +6,7 @@
extern const char git_version_string[];
extern const char git_usage_string[];
+extern void list_common_cmds_help(void);
extern void help_unknown_cmd(const char *cmd);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
extern void prune_packed_objects(int);
diff --git a/cache.h b/cache.h
index 27485d3..bfffa05 100644
--- a/cache.h
+++ b/cache.h
@@ -503,8 +503,8 @@ struct ref {
#define REF_TAGS (1u << 2)
#define CONNECT_VERBOSE (1u << 0)
-extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
-extern int finish_connect(pid_t pid);
+extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
+extern int finish_connect(struct child_process *conn);
extern int path_match(const char *path, int nr, char **match);
extern int get_ack(int fd, unsigned char *result_sha1);
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
diff --git a/color.c b/color.c
index 09d82ee..124ba33 100644
--- a/color.c
+++ b/color.c
@@ -135,39 +135,39 @@ int git_config_colorbool(const char *var, const char *value)
return git_config_bool(var, value);
}
-static int color_vprintf(const char *color, const char *fmt,
+static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
va_list args, const char *trail)
{
int r = 0;
if (*color)
- r += printf("%s", color);
- r += vprintf(fmt, args);
+ r += fprintf(fp, "%s", color);
+ r += vfprintf(fp, fmt, args);
if (*color)
- r += printf("%s", COLOR_RESET);
+ r += fprintf(fp, "%s", COLOR_RESET);
if (trail)
- r += printf("%s", trail);
+ r += fprintf(fp, "%s", trail);
return r;
}
-int color_printf(const char *color, const char *fmt, ...)
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
- r = color_vprintf(color, fmt, args, NULL);
+ r = color_vfprintf(fp, color, fmt, args, NULL);
va_end(args);
return r;
}
-int color_printf_ln(const char *color, const char *fmt, ...)
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
- r = color_vprintf(color, fmt, args, "\n");
+ r = color_vfprintf(fp, color, fmt, args, "\n");
va_end(args);
return r;
}
diff --git a/color.h b/color.h
index 88bb8ff..6809800 100644
--- a/color.h
+++ b/color.h
@@ -6,7 +6,7 @@
int git_config_colorbool(const char *var, const char *value);
void color_parse(const char *var, const char *value, char *dst);
-int color_printf(const char *color, const char *fmt, ...);
-int color_printf_ln(const char *color, const char *fmt, ...);
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
#endif /* COLOR_H */
diff --git a/commit.h b/commit.h
index b779de8..b661503 100644
--- a/commit.h
+++ b/commit.h
@@ -128,4 +128,9 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
int depth, int shallow_flag, int not_shallow_flag);
int in_merge_bases(struct commit *, struct commit **, int);
+
+extern int interactive_add(void);
+extern void add_files_to_cache(int verbose, const char *prefix, const char **files);
+extern int rerere(void);
+
#endif /* COMMIT_H */
diff --git a/connect.c b/connect.c
index 3d5c4ab..44e423d 100644
--- a/connect.c
+++ b/connect.c
@@ -468,24 +468,26 @@ char *get_port(char *host)
}
/*
- * This returns 0 if the transport protocol does not need fork(2),
- * or a process id if it does. Once done, finish the connection
+ * This returns NULL if the transport protocol does not need fork(2), or a
+ * struct child_process object if it does. Once done, finish the connection
* with finish_connect() with the value returned from this function
- * (it is safe to call finish_connect() with 0 to support the former
+ * (it is safe to call finish_connect() with NULL to support the former
* case).
*
- * Does not return a negative value on error; it just dies.
+ * If it returns, the connect is successful; it just dies on errors.
*/
-pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
+struct child_process *git_connect(int fd[2], char *url,
+ const char *prog, int flags)
{
char *host, *path = url;
char *end;
int c;
- int pipefd[2][2];
- pid_t pid;
+ struct child_process *conn;
enum protocol protocol = PROTO_LOCAL;
int free_path = 0;
char *port = NULL;
+ const char **arg;
+ struct strbuf cmd;
/* Without this we cannot rely on waitpid() to tell
* what happened to our children.
@@ -568,74 +570,68 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
free(target_host);
if (free_path)
free(path);
- return 0;
+ return NULL;
}
- if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
- die("unable to create pipe pair for communication");
- pid = fork();
- if (pid < 0)
- die("unable to fork");
- if (!pid) {
- struct strbuf cmd;
-
- strbuf_init(&cmd, MAX_CMD_LEN);
- strbuf_addstr(&cmd, prog);
- strbuf_addch(&cmd, ' ');
- sq_quote_buf(&cmd, path);
- if (cmd.len >= MAX_CMD_LEN)
- die("command line too long");
-
- dup2(pipefd[1][0], 0);
- dup2(pipefd[0][1], 1);
- close(pipefd[0][0]);
- close(pipefd[0][1]);
- close(pipefd[1][0]);
- close(pipefd[1][1]);
- if (protocol == PROTO_SSH) {
- const char *ssh, *ssh_basename;
- ssh = getenv("GIT_SSH");
- if (!ssh) ssh = "ssh";
- ssh_basename = strrchr(ssh, '/');
- if (!ssh_basename)
- ssh_basename = ssh;
- else
- ssh_basename++;
-
- if (!port)
- execlp(ssh, ssh_basename, host, cmd.buf, NULL);
- else
- execlp(ssh, ssh_basename, "-p", port, host,
- cmd.buf, NULL);
- }
- else {
- unsetenv(ALTERNATE_DB_ENVIRONMENT);
- unsetenv(DB_ENVIRONMENT);
- unsetenv(GIT_DIR_ENVIRONMENT);
- unsetenv(GIT_WORK_TREE_ENVIRONMENT);
- unsetenv(GRAFT_ENVIRONMENT);
- unsetenv(INDEX_ENVIRONMENT);
- execlp("sh", "sh", "-c", cmd.buf, NULL);
+ conn = xcalloc(1, sizeof(*conn));
+
+ strbuf_init(&cmd, MAX_CMD_LEN);
+ strbuf_addstr(&cmd, prog);
+ strbuf_addch(&cmd, ' ');
+ sq_quote_buf(&cmd, path);
+ if (cmd.len >= MAX_CMD_LEN)
+ die("command line too long");
+
+ conn->in = conn->out = -1;
+ conn->argv = arg = xcalloc(6, sizeof(*arg));
+ if (protocol == PROTO_SSH) {
+ const char *ssh = getenv("GIT_SSH");
+ if (!ssh) ssh = "ssh";
+
+ *arg++ = ssh;
+ if (port) {
+ *arg++ = "-p";
+ *arg++ = port;
}
- die("exec failed");
+ *arg++ = host;
}
- fd[0] = pipefd[0][0];
- fd[1] = pipefd[1][1];
- close(pipefd[0][1]);
- close(pipefd[1][0]);
+ else {
+ /* remove these from the environment */
+ const char *env[] = {
+ ALTERNATE_DB_ENVIRONMENT,
+ DB_ENVIRONMENT,
+ GIT_DIR_ENVIRONMENT,
+ GIT_WORK_TREE_ENVIRONMENT,
+ GRAFT_ENVIRONMENT,
+ INDEX_ENVIRONMENT,
+ NULL
+ };
+ conn->env = env;
+ *arg++ = "sh";
+ *arg++ = "-c";
+ }
+ *arg++ = cmd.buf;
+ *arg = NULL;
+
+ if (start_command(conn))
+ die("unable to fork");
+
+ fd[0] = conn->out; /* read from child's stdout */
+ fd[1] = conn->in; /* write to child's stdin */
+ strbuf_release(&cmd);
if (free_path)
free(path);
- return pid;
+ return conn;
}
-int finish_connect(pid_t pid)
+int finish_connect(struct child_process *conn)
{
- if (pid == 0)
+ int code;
+ if (!conn)
return 0;
- while (waitpid(pid, NULL, 0) < 0) {
- if (errno != EINTR)
- return -1;
- }
- return 0;
+ code = finish_command(conn);
+ free(conn->argv);
+ free(conn);
+ return code;
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e760930..599b2fc 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -346,7 +346,6 @@ __git_commands ()
ssh-*) : transport;;
stripspace) : plumbing;;
svn) : import export;;
- svnimport) : import;;
symbolic-ref) : plumbing;;
tar-tree) : deprecated;;
unpack-file) : plumbing;;
diff --git a/git-svnimport.perl b/contrib/examples/git-svnimport.perl
index ea8c1b2..ea8c1b2 100755
--- a/git-svnimport.perl
+++ b/contrib/examples/git-svnimport.perl
diff --git a/Documentation/git-svnimport.txt b/contrib/examples/git-svnimport.txt
index 71aad8b..71aad8b 100644
--- a/Documentation/git-svnimport.txt
+++ b/contrib/examples/git-svnimport.txt
diff --git a/convert.c b/convert.c
index aa95834..4df7559 100644
--- a/convert.c
+++ b/convert.c
@@ -192,48 +192,39 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
return 1;
}
-static int filter_buffer(const char *path, const char *src,
- unsigned long size, const char *cmd)
+struct filter_params {
+ const char *src;
+ unsigned long size;
+ const char *cmd;
+};
+
+static int filter_buffer(int fd, void *data)
{
/*
* Spawn cmd and feed the buffer contents through its stdin.
*/
struct child_process child_process;
- int pipe_feed[2];
+ struct filter_params *params = (struct filter_params *)data;
int write_err, status;
+ const char *argv[] = { "sh", "-c", params->cmd, NULL };
memset(&child_process, 0, sizeof(child_process));
+ child_process.argv = argv;
+ child_process.in = -1;
+ child_process.out = fd;
- if (pipe(pipe_feed) < 0) {
- error("cannot create pipe to run external filter %s", cmd);
- return 1;
- }
-
- child_process.pid = fork();
- if (child_process.pid < 0) {
- error("cannot fork to run external filter %s", cmd);
- close(pipe_feed[0]);
- close(pipe_feed[1]);
- return 1;
- }
- if (!child_process.pid) {
- dup2(pipe_feed[0], 0);
- close(pipe_feed[0]);
- close(pipe_feed[1]);
- execlp("sh", "sh", "-c", cmd, NULL);
- return 1;
- }
- close(pipe_feed[0]);
+ if (start_command(&child_process))
+ return error("cannot fork to run external filter %s", params->cmd);
- write_err = (write_in_full(pipe_feed[1], src, size) < 0);
- if (close(pipe_feed[1]))
+ write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+ if (close(child_process.in))
write_err = 1;
if (write_err)
- error("cannot feed the input to external filter %s", cmd);
+ error("cannot feed the input to external filter %s", params->cmd);
status = finish_command(&child_process);
if (status)
- error("external filter %s failed %d", cmd, -status);
+ error("external filter %s failed %d", params->cmd, -status);
return (write_err || status);
}
@@ -246,49 +237,36 @@ static int apply_filter(const char *path, const char *src, size_t len,
*
* (child --> cmd) --> us
*/
- int pipe_feed[2];
- int status, ret = 1;
- struct child_process child_process;
+ int ret = 1;
struct strbuf nbuf;
+ struct async async;
+ struct filter_params params;
if (!cmd)
return 0;
- memset(&child_process, 0, sizeof(child_process));
-
- if (pipe(pipe_feed) < 0) {
- error("cannot create pipe to run external filter %s", cmd);
- return 0;
- }
+ memset(&async, 0, sizeof(async));
+ async.proc = filter_buffer;
+ async.data = &params;
+ params.src = src;
+ params.size = len;
+ params.cmd = cmd;
fflush(NULL);
- child_process.pid = fork();
- if (child_process.pid < 0) {
- error("cannot fork to run external filter %s", cmd);
- close(pipe_feed[0]);
- close(pipe_feed[1]);
- return 0;
- }
- if (!child_process.pid) {
- dup2(pipe_feed[1], 1);
- close(pipe_feed[0]);
- close(pipe_feed[1]);
- exit(filter_buffer(path, src, len, cmd));
- }
- close(pipe_feed[1]);
+ if (start_async(&async))
+ return 0; /* error was already reported */
strbuf_init(&nbuf, 0);
- if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) {
+ if (strbuf_read(&nbuf, async.out, len) < 0) {
error("read from external filter %s failed", cmd);
ret = 0;
}
- if (close(pipe_feed[0])) {
+ if (close(async.out)) {
error("read from external filter %s failed", cmd);
ret = 0;
}
- status = finish_command(&child_process);
- if (status) {
- error("external filter %s failed %d", cmd, -status);
+ if (finish_async(&async)) {
+ error("external filter %s failed", cmd);
ret = 0;
}
diff --git a/csum-file.c b/csum-file.c
index 9ab9971..3729e73 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -8,6 +8,7 @@
* able to verify hasn't been messed with afterwards.
*/
#include "cache.h"
+#include "progress.h"
#include "csum-file.h"
static void sha1flush(struct sha1file *f, unsigned int count)
@@ -17,6 +18,7 @@ static void sha1flush(struct sha1file *f, unsigned int count)
for (;;) {
int ret = xwrite(f->fd, buf, count);
if (ret > 0) {
+ display_throughput(f->tp, ret);
buf = (char *) buf + ret;
count -= ret;
if (count)
@@ -31,22 +33,27 @@ static void sha1flush(struct sha1file *f, unsigned int count)
int sha1close(struct sha1file *f, unsigned char *result, int final)
{
+ int fd;
unsigned offset = f->offset;
if (offset) {
SHA1_Update(&f->ctx, f->buffer, offset);
sha1flush(f, offset);
f->offset = 0;
}
- if (!final)
- return 0; /* only want to flush (no checksum write, no close) */
- SHA1_Final(f->buffer, &f->ctx);
- if (result)
- hashcpy(result, f->buffer);
- sha1flush(f, 20);
- if (close(f->fd))
- die("%s: sha1 file error on close (%s)", f->name, strerror(errno));
+ if (final) {
+ /* write checksum and close fd */
+ SHA1_Final(f->buffer, &f->ctx);
+ if (result)
+ hashcpy(result, f->buffer);
+ sha1flush(f, 20);
+ if (close(f->fd))
+ die("%s: sha1 file error on close (%s)",
+ f->name, strerror(errno));
+ fd = 0;
+ } else
+ fd = f->fd;
free(f);
- return 0;
+ return fd;
}
int sha1write(struct sha1file *f, void *buf, unsigned int count)
@@ -75,6 +82,11 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count)
struct sha1file *sha1fd(int fd, const char *name)
{
+ return sha1fd_throughput(fd, name, NULL);
+}
+
+struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
+{
struct sha1file *f;
unsigned len;
@@ -89,6 +101,7 @@ struct sha1file *sha1fd(int fd, const char *name)
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->tp = tp;
f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
diff --git a/csum-file.h b/csum-file.h
index c3c792f..4d1b231 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -1,11 +1,14 @@
#ifndef CSUM_FILE_H
#define CSUM_FILE_H
+struct progress;
+
/* A SHA1-protected file */
struct sha1file {
int fd, error;
unsigned int offset, namelen;
SHA_CTX ctx;
+ struct progress *tp;
char name[PATH_MAX];
int do_crc;
uint32_t crc32;
@@ -13,6 +16,7 @@ struct sha1file {
};
extern struct sha1file *sha1fd(int fd, const char *name);
+extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
extern int sha1close(struct sha1file *, unsigned char *, int);
extern int sha1write(struct sha1file *, void *, unsigned int);
extern void crc32_begin(struct sha1file *);
diff --git a/diff.c b/diff.c
index dfb8595..a6aaaf7 100644
--- a/diff.c
+++ b/diff.c
@@ -9,6 +9,7 @@
#include "xdiff-interface.h"
#include "color.h"
#include "attr.h"
+#include "run-command.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@@ -1440,9 +1441,18 @@ struct diff_filespec *alloc_filespec(const char *path)
memset(spec, 0, sizeof(*spec));
spec->path = (char *)(spec + 1);
memcpy(spec->path, path, namelen+1);
+ spec->count = 1;
return spec;
}
+void free_filespec(struct diff_filespec *spec)
+{
+ if (!--spec->count) {
+ diff_free_filespec_data(spec);
+ free(spec);
+ }
+}
+
void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
unsigned short mode)
{
@@ -1752,40 +1762,6 @@ static void remove_tempfile_on_signal(int signo)
raise(signo);
}
-static int spawn_prog(const char *pgm, const char **arg)
-{
- pid_t pid;
- int status;
-
- fflush(NULL);
- pid = fork();
- if (pid < 0)
- die("unable to fork");
- if (!pid) {
- execvp(pgm, (char *const*) arg);
- exit(255);
- }
-
- while (waitpid(pid, &status, 0) < 0) {
- if (errno == EINTR)
- continue;
- return -1;
- }
-
- /* Earlier we did not check the exit status because
- * diff exits non-zero if files are different, and
- * we are not interested in knowing that. It was a
- * mistake which made it harder to quit a diff-*
- * session that uses the git-apply-patch-script as
- * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
- * should also exit non-zero only when it wants to
- * abort the entire diff-* session.
- */
- if (WIFEXITED(status) && !WEXITSTATUS(status))
- return 0;
- return -1;
-}
-
/* An external diff command takes:
*
* diff-cmd name infile1 infile1-sha1 infile1-mode \
@@ -1838,7 +1814,8 @@ static void run_external_diff(const char *pgm,
*arg++ = name;
}
*arg = NULL;
- retval = spawn_prog(pgm, spawn_arg);
+ fflush(NULL);
+ retval = run_command_v_opt(spawn_arg, 0);
remove_tempfile();
if (retval) {
fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -2435,10 +2412,8 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
void diff_free_filepair(struct diff_filepair *p)
{
- diff_free_filespec_data(p->one);
- diff_free_filespec_data(p->two);
- free(p->one);
- free(p->two);
+ free_filespec(p->one);
+ free_filespec(p->two);
free(p);
}
@@ -2590,9 +2565,9 @@ void diff_debug_filepair(const struct diff_filepair *p, int i)
{
diff_debug_filespec(p->one, i, "one");
diff_debug_filespec(p->two, i, "two");
- fprintf(stderr, "score %d, status %c stays %d broken %d\n",
+ fprintf(stderr, "score %d, status %c rename_used %d broken %d\n",
p->score, p->status ? p->status : '?',
- p->source_stays, p->broken_pair);
+ p->one->rename_used, p->broken_pair);
}
void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -2610,8 +2585,8 @@ void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
static void diff_resolve_rename_copy(void)
{
- int i, j;
- struct diff_filepair *p, *pp;
+ int i;
+ struct diff_filepair *p;
struct diff_queue_struct *q = &diff_queued_diff;
diff_debug_queue("resolve-rename-copy", q);
@@ -2633,27 +2608,21 @@ static void diff_resolve_rename_copy(void)
* either in-place edit or rename/copy edit.
*/
else if (DIFF_PAIR_RENAME(p)) {
- if (p->source_stays) {
- p->status = DIFF_STATUS_COPIED;
- continue;
- }
- /* See if there is some other filepair that
- * copies from the same source as us. If so
- * we are a copy. Otherwise we are either a
- * copy if the path stays, or a rename if it
- * does not, but we already handled "stays" case.
+ /*
+ * A rename might have re-connected a broken
+ * pair up, causing the pathnames to be the
+ * same again. If so, that's not a rename at
+ * all, just a modification..
+ *
+ * Otherwise, see if this source was used for
+ * multiple renames, in which case we decrement
+ * the count, and call it a copy.
*/
- for (j = i + 1; j < q->nr; j++) {
- pp = q->queue[j];
- if (strcmp(pp->one->path, p->one->path))
- continue; /* not us */
- if (!DIFF_PAIR_RENAME(pp))
- continue; /* not a rename/copy */
- /* pp is a rename/copy from the same source */
+ if (!strcmp(p->one->path, p->two->path))
+ p->status = DIFF_STATUS_MODIFIED;
+ else if (--p->one->rename_used > 0)
p->status = DIFF_STATUS_COPIED;
- break;
- }
- if (!p->status)
+ else
p->status = DIFF_STATUS_RENAMED;
}
else if (hashcmp(p->one->sha1, p->two->sha1) ||
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 142e537..f9ebea5 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "diff.h"
#include "diffcore.h"
+#include "hash.h"
/* Table of rename/copy destinations */
@@ -55,12 +56,10 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
static struct diff_rename_src {
struct diff_filespec *one;
unsigned short score; /* to remember the break score */
- unsigned src_path_left : 1;
} *rename_src;
static int rename_src_nr, rename_src_alloc;
static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
- int src_path_left,
unsigned short score)
{
int first, last;
@@ -92,33 +91,9 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
(rename_src_nr - first - 1) * sizeof(*rename_src));
rename_src[first].one = one;
rename_src[first].score = score;
- rename_src[first].src_path_left = src_path_left;
return &(rename_src[first]);
}
-static int is_exact_match(struct diff_filespec *src,
- struct diff_filespec *dst,
- int contents_too)
-{
- if (src->sha1_valid && dst->sha1_valid &&
- !hashcmp(src->sha1, dst->sha1))
- return 1;
- if (!contents_too)
- return 0;
- if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
- return 0;
- if (src->size != dst->size)
- return 0;
- if (src->sha1_valid && dst->sha1_valid)
- return !hashcmp(src->sha1, dst->sha1);
- if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
- return 0;
- if (src->size == dst->size &&
- !memcmp(src->data, dst->data, src->size))
- return 1;
- return 0;
-}
-
static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
{
int src_len = strlen(src->path), dst_len = strlen(dst->path);
@@ -169,6 +144,20 @@ static int estimate_similarity(struct diff_filespec *src,
if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
return 0;
+ /*
+ * Need to check that source and destination sizes are
+ * filled in before comparing them.
+ *
+ * If we already have "cnt_data" filled in, we know it's
+ * all good (avoid checking the size for zero, as that
+ * is a possible size - we really should have a flag to
+ * say whether the size is valid or not!)
+ */
+ if (!src->cnt_data && diff_populate_filespec(src, 0))
+ return 0;
+ if (!dst->cnt_data && diff_populate_filespec(dst, 0))
+ return 0;
+
max_size = ((src->size > dst->size) ? src->size : dst->size);
base_size = ((src->size < dst->size) ? src->size : dst->size);
delta_size = max_size - base_size;
@@ -184,11 +173,6 @@ static int estimate_similarity(struct diff_filespec *src,
if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
return 0;
- if ((!src->cnt_data && diff_populate_filespec(src, 0))
- || (!dst->cnt_data && diff_populate_filespec(dst, 0)))
- return 0; /* error but caught downstream */
-
-
delta_limit = (unsigned long)
(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
if (diffcore_count_changes(src, dst,
@@ -209,27 +193,25 @@ static int estimate_similarity(struct diff_filespec *src,
static void record_rename_pair(int dst_index, int src_index, int score)
{
- struct diff_filespec *one, *two, *src, *dst;
+ struct diff_filespec *src, *dst;
struct diff_filepair *dp;
if (rename_dst[dst_index].pair)
die("internal error: dst already matched.");
src = rename_src[src_index].one;
- one = alloc_filespec(src->path);
- fill_filespec(one, src->sha1, src->mode);
+ src->rename_used++;
+ src->count++;
dst = rename_dst[dst_index].two;
- two = alloc_filespec(dst->path);
- fill_filespec(two, dst->sha1, dst->mode);
+ dst->count++;
- dp = diff_queue(NULL, one, two);
+ dp = diff_queue(NULL, src, dst);
dp->renamed_pair = 1;
if (!strcmp(src->path, dst->path))
dp->score = rename_src[src_index].score;
else
dp->score = score;
- dp->source_stays = rename_src[src_index].src_path_left;
rename_dst[dst_index].pair = dp;
}
@@ -247,19 +229,155 @@ static int score_compare(const void *a_, const void *b_)
return b->score - a->score;
}
-static int compute_stays(struct diff_queue_struct *q,
- struct diff_filespec *one)
+struct file_similarity {
+ int src_dst, index;
+ struct diff_filespec *filespec;
+ struct file_similarity *next;
+};
+
+static int find_identical_files(struct file_similarity *src,
+ struct file_similarity *dst)
{
- int i;
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- if (strcmp(one->path, p->two->path))
- continue;
- if (DIFF_PAIR_RENAME(p)) {
- return 0; /* something else is renamed into this */
+ int renames = 0;
+
+ /*
+ * Walk over all the destinations ...
+ */
+ do {
+ struct diff_filespec *one = dst->filespec;
+ struct file_similarity *p, *best;
+ int i = 100;
+
+ /*
+ * .. to find the best source match
+ */
+ best = NULL;
+ for (p = src; p; p = p->next) {
+ struct diff_filespec *two = p->filespec;
+
+ /* False hash collission? */
+ if (hashcmp(one->sha1, two->sha1))
+ continue;
+ /* Non-regular files? If so, the modes must match! */
+ if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
+ if (one->mode != two->mode)
+ continue;
+ }
+ best = p;
+ if (basename_same(one, two))
+ break;
+
+ /* Too many identical alternatives? Pick one */
+ if (!--i)
+ break;
+ }
+ if (best) {
+ record_rename_pair(dst->index, best->index, MAX_SCORE);
+ renames++;
}
+ } while ((dst = dst->next) != NULL);
+ return renames;
+}
+
+static void free_similarity_list(struct file_similarity *p)
+{
+ while (p) {
+ struct file_similarity *entry = p;
+ p = p->next;
+ free(entry);
}
- return 1;
+}
+
+static int find_same_files(void *ptr)
+{
+ int ret;
+ struct file_similarity *p = ptr;
+ struct file_similarity *src = NULL, *dst = NULL;
+
+ /* Split the hash list up into sources and destinations */
+ do {
+ struct file_similarity *entry = p;
+ p = p->next;
+ if (entry->src_dst < 0) {
+ entry->next = src;
+ src = entry;
+ } else {
+ entry->next = dst;
+ dst = entry;
+ }
+ } while (p);
+
+ /*
+ * If we have both sources *and* destinations, see if
+ * we can match them up
+ */
+ ret = (src && dst) ? find_identical_files(src, dst) : 0;
+
+ /* Free the hashes and return the number of renames found */
+ free_similarity_list(src);
+ free_similarity_list(dst);
+ return ret;
+}
+
+static unsigned int hash_filespec(struct diff_filespec *filespec)
+{
+ unsigned int hash;
+ if (!filespec->sha1_valid) {
+ if (diff_populate_filespec(filespec, 0))
+ return 0;
+ hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
+ }
+ memcpy(&hash, filespec->sha1, sizeof(hash));
+ return hash;
+}
+
+static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec)
+{
+ void **pos;
+ unsigned int hash;
+ struct file_similarity *entry = xmalloc(sizeof(*entry));
+
+ entry->src_dst = src_dst;
+ entry->index = index;
+ entry->filespec = filespec;
+ entry->next = NULL;
+
+ hash = hash_filespec(filespec);
+ pos = insert_hash(hash, entry, table);
+
+ /* We already had an entry there? */
+ if (pos) {
+ entry->next = *pos;
+ *pos = entry;
+ }
+}
+
+/*
+ * Find exact renames first.
+ *
+ * The first round matches up the up-to-date entries,
+ * and then during the second round we try to match
+ * cache-dirty entries as well.
+ */
+static int find_exact_renames(void)
+{
+ int i;
+ struct hash_table file_table;
+
+ init_hash(&file_table);
+ for (i = 0; i < rename_src_nr; i++)
+ insert_file_table(&file_table, -1, i, rename_src[i].one);
+
+ for (i = 0; i < rename_dst_nr; i++)
+ insert_file_table(&file_table, 1, i, rename_dst[i].two);
+
+ /* Find the renames */
+ i = for_each_hash(&file_table, find_same_files);
+
+ /* .. and free the hash data structure */
+ free_hash(&file_table);
+
+ return i;
}
void diffcore_rename(struct diff_options *options)
@@ -270,12 +388,11 @@ void diffcore_rename(struct diff_options *options)
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
struct diff_score *mx;
- int i, j, rename_count, contents_too;
+ int i, j, rename_count;
int num_create, num_src, dst_cnt;
if (!minimum_score)
minimum_score = DEFAULT_RENAME_SCORE;
- rename_count = 0;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@@ -289,81 +406,66 @@ void diffcore_rename(struct diff_options *options)
locate_rename_dst(p->two, 1);
}
else if (!DIFF_FILE_VALID(p->two)) {
- /* If the source is a broken "delete", and
+ /*
+ * If the source is a broken "delete", and
* they did not really want to get broken,
* that means the source actually stays.
+ * So we increment the "rename_used" score
+ * by one, to indicate ourselves as a user
+ */
+ if (p->broken_pair && !p->score)
+ p->one->rename_used++;
+ register_rename_src(p->one, p->score);
+ }
+ else if (detect_rename == DIFF_DETECT_COPY) {
+ /*
+ * Increment the "rename_used" score by
+ * one, to indicate ourselves as a user.
*/
- int stays = (p->broken_pair && !p->score);
- register_rename_src(p->one, stays, p->score);
+ p->one->rename_used++;
+ register_rename_src(p->one, p->score);
}
- else if (detect_rename == DIFF_DETECT_COPY)
- register_rename_src(p->one, 1, p->score);
}
if (rename_dst_nr == 0 || rename_src_nr == 0)
goto cleanup; /* nothing to do */
/*
+ * We really want to cull the candidates list early
+ * with cheap tests in order to avoid doing deltas.
+ */
+ rename_count = find_exact_renames();
+
+ /* Did we only want exact renames? */
+ if (minimum_score == MAX_SCORE)
+ goto cleanup;
+
+ /*
+ * Calculate how many renames are left (but all the source
+ * files still remain as options for rename/copies!)
+ */
+ num_create = (rename_dst_nr - rename_count);
+ num_src = rename_src_nr;
+
+ /* All done? */
+ if (!num_create)
+ goto cleanup;
+
+ /*
* This basically does a test for the rename matrix not
* growing larger than a "rename_limit" square matrix, ie:
*
- * rename_dst_nr * rename_src_nr > rename_limit * rename_limit
+ * num_create * num_src > rename_limit * rename_limit
*
* but handles the potential overflow case specially (and we
* assume at least 32-bit integers)
*/
if (rename_limit <= 0 || rename_limit > 32767)
rename_limit = 32767;
- if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit)
+ if (num_create > rename_limit && num_src > rename_limit)
goto cleanup;
- if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit)
+ if (num_create * num_src > rename_limit * rename_limit)
goto cleanup;
- /* We really want to cull the candidates list early
- * with cheap tests in order to avoid doing deltas.
- * The first round matches up the up-to-date entries,
- * and then during the second round we try to match
- * cache-dirty entries as well.
- */
- for (contents_too = 0; contents_too < 2; contents_too++) {
- for (i = 0; i < rename_dst_nr; i++) {
- struct diff_filespec *two = rename_dst[i].two;
- if (rename_dst[i].pair)
- continue; /* dealt with an earlier round */
- for (j = 0; j < rename_src_nr; j++) {
- int k;
- struct diff_filespec *one = rename_src[j].one;
- if (!is_exact_match(one, two, contents_too))
- continue;
-
- /* see if there is a basename match, too */
- for (k = j; k < rename_src_nr; k++) {
- one = rename_src[k].one;
- if (basename_same(one, two) &&
- is_exact_match(one, two,
- contents_too)) {
- j = k;
- break;
- }
- }
-
- record_rename_pair(i, j, (int)MAX_SCORE);
- rename_count++;
- break; /* we are done with this entry */
- }
- }
- }
-
- /* Have we run out the created file pool? If so we can avoid
- * doing the delta matrix altogether.
- */
- if (rename_count == rename_dst_nr)
- goto cleanup;
-
- if (minimum_score == MAX_SCORE)
- goto cleanup;
-
- num_create = (rename_dst_nr - rename_count);
- num_src = rename_src_nr;
mx = xmalloc(sizeof(*mx) * num_create * num_src);
for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
int base = dst_cnt * num_src;
@@ -452,16 +554,7 @@ void diffcore_rename(struct diff_options *options)
pair_to_free = p;
}
else {
- for (j = 0; j < rename_dst_nr; j++) {
- if (!rename_dst[j].pair)
- continue;
- if (strcmp(rename_dst[j].pair->
- one->path,
- p->one->path))
- continue;
- break;
- }
- if (j < rename_dst_nr)
+ if (p->one->rename_used)
/* this path remains */
pair_to_free = p;
}
@@ -487,27 +580,8 @@ void diffcore_rename(struct diff_options *options)
*q = outq;
diff_debug_queue("done collapsing", q);
- /* We need to see which rename source really stays here;
- * earlier we only checked if the path is left in the result,
- * but even if a path remains in the result, if that is coming
- * from copying something else on top of it, then the original
- * source is lost and does not stay.
- */
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- if (DIFF_PAIR_RENAME(p) && p->source_stays) {
- /* If one appears as the target of a rename-copy,
- * then mark p->source_stays = 0; otherwise
- * leave it as is.
- */
- p->source_stays = compute_stays(q, p->one);
- }
- }
-
- for (i = 0; i < rename_dst_nr; i++) {
- diff_free_filespec_data(rename_dst[i].two);
- free(rename_dst[i].two);
- }
+ for (i = 0; i < rename_dst_nr; i++)
+ free_filespec(rename_dst[i].two);
free(rename_dst);
rename_dst = NULL;
diff --git a/diffcore.h b/diffcore.h
index eb618b1..cc96c20 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -29,7 +29,9 @@ struct diff_filespec {
void *cnt_data;
const char *funcname_pattern_ident;
unsigned long size;
+ int count; /* Reference count */
int xfrm_flags; /* for use by the xfrm */
+ int rename_used; /* Count of rename users */
unsigned short mode; /* file mode */
unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
* if false, use the name and read from
@@ -43,6 +45,7 @@ struct diff_filespec {
};
extern struct diff_filespec *alloc_filespec(const char *);
+extern void free_filespec(struct diff_filespec *);
extern void fill_filespec(struct diff_filespec *, const unsigned char *,
unsigned short);
@@ -56,7 +59,6 @@ struct diff_filepair {
struct diff_filespec *two;
unsigned short int score;
char status; /* M C R N D U (see Documentation/diff-format.txt) */
- unsigned source_stays : 1; /* all of R/C are copies */
unsigned broken_pair : 1;
unsigned renamed_pair : 1;
unsigned is_unmerged : 1;
diff --git a/exec_cmd.c b/exec_cmd.c
index 9b74ed2..2d0a758 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -5,11 +5,11 @@
extern char **environ;
static const char *builtin_exec_path = GIT_EXEC_PATH;
-static const char *current_exec_path;
+static const char *argv_exec_path;
-void git_set_exec_path(const char *exec_path)
+void git_set_argv_exec_path(const char *exec_path)
{
- current_exec_path = exec_path;
+ argv_exec_path = exec_path;
}
@@ -18,8 +18,8 @@ const char *git_exec_path(void)
{
const char *env;
- if (current_exec_path)
- return current_exec_path;
+ if (argv_exec_path)
+ return argv_exec_path;
env = getenv(EXEC_PATH_ENVIRONMENT);
if (env && *env) {
@@ -29,85 +29,69 @@ const char *git_exec_path(void)
return builtin_exec_path;
}
+static void add_path(struct strbuf *out, const char *path)
+{
+ if (path && *path) {
+ if (is_absolute_path(path))
+ strbuf_addstr(out, path);
+ else
+ strbuf_addstr(out, make_absolute_path(path));
+
+ strbuf_addch(out, ':');
+ }
+}
+
+void setup_path(const char *cmd_path)
+{
+ const char *old_path = getenv("PATH");
+ struct strbuf new_path;
+
+ strbuf_init(&new_path, 0);
+
+ add_path(&new_path, argv_exec_path);
+ add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
+ add_path(&new_path, builtin_exec_path);
+ add_path(&new_path, cmd_path);
+
+ if (old_path)
+ strbuf_addstr(&new_path, old_path);
+ else
+ strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
+
+ setenv("PATH", new_path.buf, 1);
+
+ strbuf_release(&new_path);
+}
int execv_git_cmd(const char **argv)
{
- char git_command[PATH_MAX + 1];
- int i;
- const char *paths[] = { current_exec_path,
- getenv(EXEC_PATH_ENVIRONMENT),
- builtin_exec_path };
-
- for (i = 0; i < ARRAY_SIZE(paths); ++i) {
- size_t len;
- int rc;
- const char *exec_dir = paths[i];
- const char *tmp;
-
- if (!exec_dir || !*exec_dir) continue;
-
- if (*exec_dir != '/') {
- if (!getcwd(git_command, sizeof(git_command))) {
- fprintf(stderr, "git: cannot determine "
- "current directory: %s\n",
- strerror(errno));
- break;
- }
- len = strlen(git_command);
-
- /* Trivial cleanup */
- while (!prefixcmp(exec_dir, "./")) {
- exec_dir += 2;
- while (*exec_dir == '/')
- exec_dir++;
- }
-
- rc = snprintf(git_command + len,
- sizeof(git_command) - len, "/%s",
- exec_dir);
- if (rc < 0 || rc >= sizeof(git_command) - len) {
- fprintf(stderr, "git: command name given "
- "is too long.\n");
- break;
- }
- } else {
- if (strlen(exec_dir) + 1 > sizeof(git_command)) {
- fprintf(stderr, "git: command name given "
- "is too long.\n");
- break;
- }
- strcpy(git_command, exec_dir);
- }
-
- len = strlen(git_command);
- rc = snprintf(git_command + len, sizeof(git_command) - len,
- "/git-%s", argv[0]);
- if (rc < 0 || rc >= sizeof(git_command) - len) {
- fprintf(stderr,
- "git: command name given is too long.\n");
- break;
- }
+ struct strbuf cmd;
+ const char *tmp;
- /* argv[0] must be the git command, but the argv array
- * belongs to the caller, and my be reused in
- * subsequent loop iterations. Save argv[0] and
- * restore it on error.
- */
+ strbuf_init(&cmd, 0);
+ strbuf_addf(&cmd, "git-%s", argv[0]);
- tmp = argv[0];
- argv[0] = git_command;
+ /*
+ * argv[0] must be the git command, but the argv array
+ * belongs to the caller, and may be reused in
+ * subsequent loop iterations. Save argv[0] and
+ * restore it on error.
+ */
+ tmp = argv[0];
+ argv[0] = cmd.buf;
- trace_argv_printf(argv, -1, "trace: exec:");
+ trace_argv_printf(argv, -1, "trace: exec:");
- /* execve() can only ever return if it fails */
- execve(git_command, (char **)argv, environ);
+ /* execvp() can only ever return if it fails */
+ execvp(cmd.buf, (char **)argv);
- trace_printf("trace: exec failed: %s\n", strerror(errno));
+ trace_printf("trace: exec failed: %s\n", strerror(errno));
- argv[0] = tmp;
- }
- return -1;
+ argv[0] = tmp;
+ strbuf_release(&cmd);
+
+ return -1;
}
diff --git a/exec_cmd.h b/exec_cmd.h
index 849a839..a892355 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -1,8 +1,9 @@
#ifndef GIT_EXEC_CMD_H
#define GIT_EXEC_CMD_H
-extern void git_set_exec_path(const char *exec_path);
+extern void git_set_argv_exec_path(const char *exec_path);
extern const char* git_exec_path(void);
+extern void setup_path(const char *);
extern int execv_git_cmd(const char **argv); /* NULL terminated */
extern int execl_git_cmd(const char *cmd, ...);
diff --git a/git-bisect.sh b/git-bisect.sh
index 388887a..b74f44d 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -1,12 +1,14 @@
#!/bin/sh
-USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
+USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
reset bisect state and start bisection.
git bisect bad [<rev>]
mark <rev> a known-bad revision.
git bisect good [<rev>...]
mark <rev>... known-good revisions.
+git bisect skip [<rev>...]
+ mark <rev>... untestable revisions.
git bisect next
find next bisection to test and check it out.
git bisect reset [<branch>]
@@ -64,7 +66,7 @@ bisect_start() {
branch=`cat "$GIT_DIR/head-name"`
else
branch=master
- fi
+ fi
git checkout $branch || exit
;;
refs/heads/*)
@@ -95,75 +97,74 @@ bisect_start() {
arg="$1"
case "$arg" in
--)
- shift
+ shift
break
;;
*)
- rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
+ rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
test $has_double_dash -eq 1 &&
die "'$arg' does not appear to be a valid revision"
break
}
- if [ $bad_seen -eq 0 ]; then
- bad_seen=1
- bisect_write_bad "$rev"
- else
- bisect_write_good "$rev"
- fi
- shift
+ case $bad_seen in
+ 0) state='bad' ; bad_seen=1 ;;
+ *) state='good' ;;
+ esac
+ bisect_write "$state" "$rev" 'nolog'
+ shift
;;
esac
- done
+ done
sq "$@" >"$GIT_DIR/BISECT_NAMES"
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
bisect_auto_next
}
-bisect_bad() {
- bisect_autostart
- case "$#" in
- 0)
- rev=$(git rev-parse --verify HEAD) ;;
- 1)
- rev=$(git rev-parse --verify "$1^{commit}") ;;
- *)
- usage ;;
- esac || exit
- bisect_write_bad "$rev"
- echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
- bisect_auto_next
-}
-
-bisect_write_bad() {
- rev="$1"
- echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+bisect_write() {
+ state="$1"
+ rev="$2"
+ nolog="$3"
+ case "$state" in
+ bad) tag="$state" ;;
+ good|skip) tag="$state"-"$rev" ;;
+ *) die "Bad bisect_write argument: $state" ;;
+ esac
+ echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
+ echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
}
-bisect_good() {
+bisect_state() {
bisect_autostart
- case "$#" in
- 0) revs=$(git rev-parse --verify HEAD) || exit ;;
- *) revs=$(git rev-parse --revs-only --no-flags "$@") &&
- test '' != "$revs" || die "Bad rev input: $@" ;;
+ state=$1
+ case "$#,$state" in
+ 0,*)
+ die "Please call 'bisect_state' with at least one argument." ;;
+ 1,bad|1,good|1,skip)
+ rev=$(git rev-parse --verify HEAD) ||
+ die "Bad rev input: HEAD"
+ bisect_write "$state" "$rev" ;;
+ 2,bad)
+ rev=$(git rev-parse --verify "$2^{commit}") ||
+ die "Bad rev input: $2"
+ bisect_write "$state" "$rev" ;;
+ *,good|*,skip)
+ shift
+ revs=$(git rev-parse --revs-only --no-flags "$@") &&
+ test '' != "$revs" || die "Bad rev input: $@"
+ for rev in $revs
+ do
+ rev=$(git rev-parse --verify "$rev^{commit}") ||
+ die "Bad rev commit: $rev^{commit}"
+ bisect_write "$state" "$rev"
+ done ;;
+ *)
+ usage ;;
esac
- for rev in $revs
- do
- rev=$(git rev-parse --verify "$rev^{commit}") || exit
- bisect_write_good "$rev"
- echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
-
- done
bisect_auto_next
}
-bisect_write_good() {
- rev="$1"
- echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
-}
-
bisect_next_check() {
missing_good= missing_bad=
git show-ref -q --verify refs/bisect/bad || missing_bad=t
@@ -206,17 +207,97 @@ bisect_auto_next() {
bisect_next_check && bisect_next || :
}
+filter_skipped() {
+ _eval="$1"
+ _skip="$2"
+
+ if [ -z "$_skip" ]; then
+ eval $_eval
+ return
+ fi
+
+ # Let's parse the output of:
+ # "git rev-list --bisect-vars --bisect-all ..."
+ eval $_eval | while read hash line
+ do
+ case "$VARS,$FOUND,$TRIED,$hash" in
+ # We display some vars.
+ 1,*,*,*) echo "$hash $line" ;;
+
+ # Split line.
+ ,*,*,---*) ;;
+
+ # We had nothing to search.
+ ,,,bisect_rev*)
+ echo "bisect_rev="
+ VARS=1
+ ;;
+
+ # We did not find a good bisect rev.
+ # This should happen only if the "bad"
+ # commit is also a "skip" commit.
+ ,,*,bisect_rev*)
+ echo "bisect_rev=$TRIED"
+ VARS=1
+ ;;
+
+ # We are searching.
+ ,,*,*)
+ TRIED="${TRIED:+$TRIED|}$hash"
+ case "$_skip" in
+ *$hash*) ;;
+ *)
+ echo "bisect_rev=$hash"
+ echo "bisect_tried=\"$TRIED\""
+ FOUND=1
+ ;;
+ esac
+ ;;
+
+ # We have already found a rev to be tested.
+ ,1,*,bisect_rev*) VARS=1 ;;
+ ,1,*,*) ;;
+
+ # ???
+ *) die "filter_skipped error " \
+ "VARS: '$VARS' " \
+ "FOUND: '$FOUND' " \
+ "TRIED: '$TRIED' " \
+ "hash: '$hash' " \
+ "line: '$line'"
+ ;;
+ esac
+ done
+}
+
+exit_if_skipped_commits () {
+ _tried=$1
+ if expr "$_tried" : ".*[|].*" > /dev/null ; then
+ echo "There are only 'skip'ped commit left to test."
+ echo "The first bad commit could be any of:"
+ echo "$_tried" | sed -e 's/[|]/\n/g'
+ echo "We cannot bisect more!"
+ exit 2
+ fi
+}
+
bisect_next() {
- case "$#" in 0) ;; *) usage ;; esac
+ case "$#" in 0) ;; *) usage ;; esac
bisect_autostart
bisect_next_check good
+ skip=$(git for-each-ref --format='%(objectname)' \
+ "refs/bisect/skip-*" | tr '[\012]' ' ') || exit
+
+ BISECT_OPT=''
+ test -n "$skip" && BISECT_OPT='--bisect-all'
+
bad=$(git rev-parse --verify refs/bisect/bad) &&
good=$(git for-each-ref --format='^%(objectname)' \
"refs/bisect/good-*" | tr '[\012]' ' ') &&
- eval="git rev-list --bisect-vars $good $bad --" &&
+ eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
- eval=$(eval "$eval") &&
+ eval=$(filter_skipped "$eval" "$skip") &&
eval "$eval" || exit
if [ -z "$bisect_rev" ]; then
@@ -224,11 +305,16 @@ bisect_next() {
exit 1
fi
if [ "$bisect_rev" = "$bad" ]; then
+ exit_if_skipped_commits "$bisect_tried"
echo "$bisect_rev is first bad commit"
git diff-tree --pretty $bisect_rev
exit 0
fi
+ # We should exit here only if the "bad"
+ # commit is also a "skip" commit (see above).
+ exit_if_skipped_commits "$bisect_rev"
+
echo "Bisecting: $bisect_nr revisions left to test after this"
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
git checkout -q new-bisect || exit
@@ -250,12 +336,10 @@ bisect_reset() {
else
branch=master
fi ;;
- 1) git show-ref --verify --quiet -- "refs/heads/$1" || {
- echo >&2 "$1 does not seem to be a valid branch"
- exit 1
- }
+ 1) git show-ref --verify --quiet -- "refs/heads/$1" ||
+ die "$1 does not seem to be a valid branch"
branch="$1" ;;
- *)
+ *)
usage ;;
esac
if git checkout "$branch"; then
@@ -273,10 +357,7 @@ bisect_clean_state() {
}
bisect_replay () {
- test -r "$1" || {
- echo >&2 "cannot read $1 for replaying"
- exit 1
- }
+ test -r "$1" || die "cannot read $1 for replaying"
bisect_reset
while read bisect command rev
do
@@ -284,21 +365,11 @@ bisect_replay () {
case "$command" in
start)
cmd="bisect_start $rev"
- eval "$cmd"
- ;;
- good)
- echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
- ;;
- bad)
- echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
- ;;
+ eval "$cmd" ;;
+ good|bad|skip)
+ bisect_write "$command" "$rev" ;;
*)
- echo >&2 "?? what are you talking about?"
- exit 1 ;;
+ die "?? what are you talking about?" ;;
esac
done <"$1"
bisect_auto_next
@@ -320,24 +391,31 @@ bisect_run () {
exit $res
fi
- # Use "bisect_good" or "bisect_bad"
- # depending on run success or failure.
- if [ $res -gt 0 ]; then
- next_bisect='bisect_bad'
+ # Find current state depending on run success or failure.
+ # A special exit code of 125 means cannot test.
+ if [ $res -eq 125 ]; then
+ state='skip'
+ elif [ $res -gt 0 ]; then
+ state='bad'
else
- next_bisect='bisect_good'
+ state='good'
fi
- # We have to use a subshell because bisect_good or
- # bisect_bad functions can exit.
- ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
+ # We have to use a subshell because "bisect_state" can exit.
+ ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
res=$?
cat "$GIT_DIR/BISECT_RUN"
+ if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+ > /dev/null; then
+ echo >&2 "bisect run cannot continue any more"
+ exit $res
+ fi
+
if [ $res -ne 0 ]; then
echo >&2 "bisect run failed:"
- echo >&2 "$next_bisect exited with error code $res"
+ echo >&2 "'bisect_state $state' exited with error code $res"
exit $res
fi
@@ -359,10 +437,8 @@ case "$#" in
case "$cmd" in
start)
bisect_start "$@" ;;
- bad)
- bisect_bad "$@" ;;
- good)
- bisect_good "$@" ;;
+ bad|good|skip)
+ bisect_state "$cmd" "$@" ;;
next)
# Not sure we want "next" at the UI level anymore.
bisect_next "$@" ;;
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index f284c88..26844af 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -82,6 +82,7 @@ foreach my $line (@commit) {
}
}
+my $noparent = "0000000000000000000000000000000000000000";
if ($parent) {
my $found;
# double check that it's a valid parent
@@ -95,8 +96,10 @@ if ($parent) {
} else { # we don't have a parent from the cmdline...
if (@parents == 1) { # it's safe to get it from the commit
$parent = $parents[0];
- } else { # or perhaps not!
- die "This commit has more than one parent -- please name the parent you want to use explicitly";
+ } elsif (@parents == 0) { # there is no parent
+ $parent = $noparent;
+ } else { # cannot choose automatically from multiple parents
+ die "This commit has more than one parent -- please name the parent you want to use explicitly";
}
}
@@ -116,7 +119,11 @@ if ($opt_a) {
}
close MSG;
-`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+if ($parent eq $noparent) {
+ `git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+} else {
+ `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+}
## apply non-binary changes
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 9f4f313..a68b403 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -192,10 +192,10 @@ merge_file () {
case "$merge_tool" in
kdiff3)
if base_present ; then
- (kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
+ ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
-o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
else
- (kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
+ ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
-o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
@@ -203,35 +203,35 @@ merge_file () {
;;
tkdiff)
if base_present ; then
- tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
else
- tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
fi
status=$?
save_backup
;;
meld|vimdiff)
touch "$BACKUP"
- $merge_tool -- "$LOCAL" "$path" "$REMOTE"
+ "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
check_unchanged
save_backup
;;
gvimdiff)
touch "$BACKUP"
- gvimdiff -f -- "$LOCAL" "$path" "$REMOTE"
+ "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
check_unchanged
save_backup
;;
xxdiff)
touch "$BACKUP"
if base_present ; then
- xxdiff -X --show-merged-pane \
+ "$merge_tool_path" -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
else
- xxdiff -X --show-merged-pane \
+ "$merge_tool_path" -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
@@ -243,18 +243,28 @@ merge_file () {
opendiff)
touch "$BACKUP"
if base_present; then
- opendiff "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
else
- opendiff "$LOCAL" "$REMOTE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+ fi
+ check_unchanged
+ save_backup
+ ;;
+ ecmerge)
+ touch "$BACKUP"
+ if base_present; then
+ "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+ else
+ "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
fi
check_unchanged
save_backup
;;
emerge)
if base_present ; then
- emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
else
- emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
fi
status=$?
save_backup
@@ -297,17 +307,38 @@ do
shift
done
+valid_tool() {
+ case "$1" in
+ kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
+ ;; # happy
+ *)
+ return 1
+ ;;
+ esac
+}
+
+init_merge_tool_path() {
+ merge_tool_path=`git config mergetool.$1.path`
+ if test -z "$merge_tool_path" ; then
+ case "$1" in
+ emerge)
+ merge_tool_path=emacs
+ ;;
+ *)
+ merge_tool_path=$1
+ ;;
+ esac
+ fi
+}
+
+
if test -z "$merge_tool"; then
merge_tool=`git config merge.tool`
- case "$merge_tool" in
- kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | "")
- ;; # happy
- *)
+ if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
- ;;
- esac
+ fi
fi
if test -z "$merge_tool" ; then
@@ -329,40 +360,30 @@ if test -z "$merge_tool" ; then
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
for i in $merge_tool_candidates; do
- if test $i = emerge ; then
- cmd=emacs
- else
- cmd=$i
- fi
- if type $cmd > /dev/null 2>&1; then
+ init_merge_tool_path $i
+ if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
- echo "No available merge resolution programs available."
+ echo "No known merge resolution program available."
exit 1
fi
+else
+ if ! valid_tool "$merge_tool"; then
+ echo >&2 "Unknown merge_tool $merge_tool"
+ exit 1
+ fi
+
+ init_merge_tool_path "$merge_tool"
+
+ if ! type "$merge_tool_path" > /dev/null 2>&1; then
+ echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
+ exit 1
+ fi
fi
-case "$merge_tool" in
- kdiff3|tkdiff|meld|xxdiff|vimdiff|gvimdiff|opendiff)
- if ! type "$merge_tool" > /dev/null 2>&1; then
- echo "The merge tool $merge_tool is not available"
- exit 1
- fi
- ;;
- emerge)
- if ! type "emacs" > /dev/null 2>&1; then
- echo "Emacs is not available"
- exit 1
- fi
- ;;
- *)
- echo "Unknown merge tool: $merge_tool"
- exit 1
- ;;
-esac
if test $# -eq 0 ; then
files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 0dd77b4..76dc679 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -116,7 +116,7 @@ pick_one () {
sha1=$(git rev-parse --short $sha1)
output warn Fast forward to $sha1
else
- output git cherry-pick $STRATEGY "$@"
+ output git cherry-pick "$@"
fi
}
@@ -172,6 +172,8 @@ pick_one_preserving_merges () {
author_script=$(get_author_ident_from_commit $sha1)
eval "$author_script"
msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+ # No point in merging the first parent, that's HEAD
+ new_parents=${new_parents# $first_parent}
# NEEDSWORK: give rerere a chance
if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
@@ -184,7 +186,7 @@ pick_one_preserving_merges () {
fi
;;
*)
- output git cherry-pick $STRATEGY "$@" ||
+ output git cherry-pick "$@" ||
die_with_patch $sha1 "Could not pick $sha1"
;;
esac
@@ -387,7 +389,6 @@ do
output git reset --hard && do_rest
;;
-s|--strategy)
- shift
case "$#,$1" in
*,*=*)
STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
diff --git a/git-rebase.sh b/git-rebase.sh
index 1583402..224cca9 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -59,7 +59,7 @@ continue_merge () {
die "$RESOLVEMSG"
fi
- cmt=`cat $dotest/current`
+ cmt=`cat "$dotest/current"`
if ! git diff-index --quiet HEAD
then
if ! git-commit -C "$cmt"
@@ -84,14 +84,14 @@ continue_merge () {
}
call_merge () {
- cmt="$(cat $dotest/cmt.$1)"
+ cmt="$(cat "$dotest/cmt.$1")"
echo "$cmt" > "$dotest/current"
hd=$(git rev-parse --verify HEAD)
cmt_name=$(git symbolic-ref HEAD)
- msgnum=$(cat $dotest/msgnum)
- end=$(cat $dotest/end)
+ msgnum=$(cat "$dotest/msgnum")
+ end=$(cat "$dotest/end")
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
- eval GITHEAD_$hd='"$(cat $dotest/onto_name)"'
+ eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
export GITHEAD_$cmt GITHEAD_$hd
git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
rv=$?
@@ -140,10 +140,10 @@ do
}
if test -d "$dotest"
then
- prev_head="`cat $dotest/prev_head`"
- end="`cat $dotest/end`"
- msgnum="`cat $dotest/msgnum`"
- onto="`cat $dotest/onto`"
+ prev_head=$(cat "$dotest/prev_head")
+ end=$(cat "$dotest/end")
+ msgnum=$(cat "$dotest/msgnum")
+ onto=$(cat "$dotest/onto")
continue_merge
while test "$msgnum" -le "$end"
do
@@ -160,11 +160,11 @@ do
if test -d "$dotest"
then
git rerere clear
- prev_head="`cat $dotest/prev_head`"
- end="`cat $dotest/end`"
- msgnum="`cat $dotest/msgnum`"
+ prev_head=$(cat "$dotest/prev_head")
+ end=$(cat "$dotest/end")
+ msgnum=$(cat "$dotest/msgnum")
msgnum=$(($msgnum + 1))
- onto="`cat $dotest/onto`"
+ onto=$(cat "$dotest/onto")
while test "$msgnum" -le "$end"
do
call_merge "$msgnum"
diff --git a/git-repack.sh b/git-repack.sh
index e72adc4..7220635 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -83,9 +83,6 @@ for name in $names ; do
fullbases="$fullbases pack-$name"
chmod a-w "$PACKTMP-$name.pack"
chmod a-w "$PACKTMP-$name.idx"
- if test "$quiet" != '-q'; then
- echo "Pack pack-$name created."
- fi
mkdir -p "$PACKDIR" || exit
for sfx in pack idx
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 3c325fd..86d7d4c 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -110,7 +110,7 @@ esac
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${GIT_DIR=.git}
- GIT_DIR=$(GIT_DIR="$GIT_DIR" git rev-parse --git-dir) || {
+ test -z "$(git rev-parse --show-cdup)" || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
diff --git a/git.c b/git.c
index 23a430c..4e10581 100644
--- a/git.c
+++ b/git.c
@@ -6,28 +6,6 @@
const char git_usage_string[] =
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
-static void prepend_to_path(const char *dir, int len)
-{
- const char *old_path = getenv("PATH");
- char *path;
- int path_len = len;
-
- if (!old_path)
- old_path = "/usr/local/bin:/usr/bin:/bin";
-
- path_len = len + strlen(old_path) + 1;
-
- path = xmalloc(path_len + 1);
-
- memcpy(path, dir, len);
- path[len] = ':';
- memcpy(path + len + 1, old_path, path_len - len);
-
- setenv("PATH", path, 1);
-
- free(path);
-}
-
static int handle_options(const char*** argv, int* argc, int* envchanged)
{
int handled = 0;
@@ -51,7 +29,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
if (!prefixcmp(cmd, "--exec-path")) {
cmd += 11;
if (*cmd == '=')
- git_set_exec_path(cmd + 1);
+ git_set_argv_exec_path(cmd + 1);
else {
puts(git_exec_path());
exit(0);
@@ -408,7 +386,7 @@ int main(int argc, const char **argv)
{
const char *cmd = argv[0] ? argv[0] : "git-help";
char *slash = strrchr(cmd, '/');
- const char *exec_path = NULL;
+ const char *cmd_path = NULL;
int done_alias = 0;
/*
@@ -418,10 +396,7 @@ int main(int argc, const char **argv)
*/
if (slash) {
*slash++ = 0;
- if (*cmd == '/')
- exec_path = cmd;
- else
- exec_path = xstrdup(make_absolute_path(cmd));
+ cmd_path = cmd;
cmd = slash;
}
@@ -450,23 +425,20 @@ int main(int argc, const char **argv)
if (!prefixcmp(argv[0], "--"))
argv[0] += 2;
} else {
- /* Default command: "help" */
- argv[0] = "help";
- argc = 1;
+ /* The user didn't specify a command; give them help */
+ printf("usage: %s\n\n", git_usage_string);
+ list_common_cmds_help();
+ exit(1);
}
cmd = argv[0];
/*
- * We execute external git command via execv_git_cmd(),
- * which looks at "--exec-path" option, GIT_EXEC_PATH
- * environment, and $(gitexecdir) in Makefile while built,
- * in this order. For scripted commands, we prepend
- * the value of the exec_path variable to the PATH.
+ * We use PATH to find git commands, but we prepend some higher
+ * precidence paths: the "--exec-path" option, the GIT_EXEC_PATH
+ * environment, and the $(gitexecdir) from the Makefile at build
+ * time.
*/
- if (exec_path)
- prepend_to_path(exec_path, strlen(exec_path));
- exec_path = git_exec_path();
- prepend_to_path(exec_path, strlen(exec_path));
+ setup_path(cmd_path);
while (1) {
/* See if it's an internal command */
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 6328e26..9cd5b0a 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -116,7 +116,7 @@ GITWEB_CONFIG file:
$feature{'pickaxe'}{'default'} = [1];
$feature{'pickaxe'}{'override'} = 1;
- $feature{'snapshot'}{'default'} = ['x-gzip', 'gz', 'gzip'];
+ $feature{'snapshot'}{'default'} = ['zip', 'tgz'];
$feature{'snapshot'}{'override'} = 1;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 48e21da..2e00756 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -846,6 +846,23 @@ sub chop_str {
return "$body$tail";
}
+# takes the same arguments as chop_str, but also wraps a <span> around the
+# result with a title attribute if it does get chopped. Additionally, the
+# string is HTML-escaped.
+sub chop_and_escape_str {
+ my $str = shift;
+ my $len = shift;
+ my $add_len = shift || 10;
+
+ my $chopped = chop_str($str, $len, $add_len);
+ if ($chopped eq $str) {
+ return esc_html($chopped);
+ } else {
+ return qq{<span title="} . esc_html($str) . qq{">} .
+ esc_html($chopped) . qq{</span>};
+ }
+}
+
## ----------------------------------------------------------------------
## functions returning short strings
@@ -2000,6 +2017,19 @@ sub parse_difftree_raw_line {
return wantarray ? %res : \%res;
}
+# wrapper: return parsed line of git-diff-tree "raw" output
+# (the argument might be raw line, or parsed info)
+sub parsed_difftree_line {
+ my $line_or_ref = shift;
+
+ if (ref($line_or_ref) eq "HASH") {
+ # pre-parsed (or generated by hand)
+ return $line_or_ref;
+ } else {
+ return parse_difftree_raw_line($line_or_ref);
+ }
+}
+
# parse line of git-ls-tree output
sub parse_ls_tree_line ($;%) {
my $line = shift;
@@ -2043,6 +2073,7 @@ sub parse_from_to_diffinfo {
}
}
} else {
+ # ordinary (not combined) diff
$from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
if ($diffinfo->{'status'} ne "A") { # not new (added) file
$from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
@@ -2766,6 +2797,7 @@ sub git_print_tree_entry {
## ......................................................................
## functions printing large fragments of HTML
+# get pre-image filenames for merge (combined) diff
sub fill_from_file_info {
my ($diff, @parents) = @_;
@@ -2782,28 +2814,25 @@ sub fill_from_file_info {
return $diff;
}
-# parameters can be strings, or references to arrays of strings
-sub from_ids_eq {
- my ($a, $b) = @_;
-
- if (ref($a) eq "ARRAY" && ref($b) eq "ARRAY" && @$a == @$b) {
- for (my $i = 0; $i < @$a; ++$i) {
- return 0 unless ($a->[$i] eq $b->[$i]);
- }
- return 1;
- } elsif (!ref($a) && !ref($b)) {
- return $a eq $b;
- } else {
- return 0;
- }
-}
-
+# is current raw difftree line of file deletion
sub is_deleted {
my $diffinfo = shift;
return $diffinfo->{'to_id'} eq ('0' x 40);
}
+# does patch correspond to [previous] difftree raw line
+# $diffinfo - hashref of parsed raw diff format
+# $patchinfo - hashref of parsed patch diff format
+# (the same keys as in $diffinfo)
+sub is_patch_split {
+ my ($diffinfo, $patchinfo) = @_;
+
+ return defined $diffinfo && defined $patchinfo
+ && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
+}
+
+
sub git_difftree_body {
my ($difftree, $hash, @parents) = @_;
my ($parent) = $parents[0];
@@ -2840,13 +2869,7 @@ sub git_difftree_body {
my $alternate = 1;
my $patchno = 0;
foreach my $line (@{$difftree}) {
- my $diff;
- if (ref($line) eq "HASH") {
- # pre-parsed (or generated by hand)
- $diff = $line;
- } else {
- $diff = parse_difftree_raw_line($line);
- }
+ my $diff = parsed_difftree_line($line);
if ($alternate) {
print "<tr class=\"dark\">\n";
@@ -3117,10 +3140,12 @@ sub git_patchset_body {
my ($fd, $difftree, $hash, @hash_parents) = @_;
my ($hash_parent) = $hash_parents[0];
+ my $is_combined = (@hash_parents > 1);
my $patch_idx = 0;
my $patch_number = 0;
my $patch_line;
my $diffinfo;
+ my $to_name;
my (%from, %to);
print "<div class=\"patchset\">\n";
@@ -3134,73 +3159,46 @@ sub git_patchset_body {
PATCH:
while ($patch_line) {
- my @diff_header;
- my ($from_id, $to_id);
-
- # git diff header
- #assert($patch_line =~ m/^diff /) if DEBUG;
- #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
- $patch_number++;
- push @diff_header, $patch_line;
-
- # extended diff header
- EXTENDED_HEADER:
- while ($patch_line = <$fd>) {
- chomp $patch_line;
-
- last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
-
- if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
- $from_id = $1;
- $to_id = $2;
- } elsif ($patch_line =~ m/^index ((?:[0-9a-fA-F]{40},)+[0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
- $from_id = [ split(',', $1) ];
- $to_id = $2;
- }
- push @diff_header, $patch_line;
+ # parse "git diff" header line
+ if ($patch_line =~ m/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/) {
+ # $1 is from_name, which we do not use
+ $to_name = unquote($2);
+ $to_name =~ s!^b/!!;
+ } elsif ($patch_line =~ m/^diff --(cc|combined) ("?.*"?)$/) {
+ # $1 is 'cc' or 'combined', which we do not use
+ $to_name = unquote($2);
+ } else {
+ $to_name = undef;
}
- my $last_patch_line = $patch_line;
# check if current patch belong to current raw line
# and parse raw git-diff line if needed
- if (defined $diffinfo &&
- defined $from_id && defined $to_id &&
- from_ids_eq($diffinfo->{'from_id'}, $from_id) &&
- $diffinfo->{'to_id'} eq $to_id) {
+ if (is_patch_split($diffinfo, { 'to_file' => $to_name })) {
# this is continuation of a split patch
print "<div class=\"patch cont\">\n";
} else {
# advance raw git-diff output if needed
$patch_idx++ if defined $diffinfo;
- # compact combined diff output can have some patches skipped
- # find which patch (using pathname of result) we are at now
- my $to_name;
- if ($diff_header[0] =~ m!^diff --cc "?(.*)"?$!) {
- $to_name = $1;
- }
-
- do {
- # read and prepare patch information
- if (ref($difftree->[$patch_idx]) eq "HASH") {
- # pre-parsed (or generated by hand)
- $diffinfo = $difftree->[$patch_idx];
- } else {
- $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
- }
+ # read and prepare patch information
+ $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
- # check if current raw line has no patch (it got simplified)
- if (defined $to_name && $to_name ne $diffinfo->{'to_file'}) {
+ # compact combined diff output can have some patches skipped
+ # find which patch (using pathname of result) we are at now;
+ if ($is_combined) {
+ while ($to_name ne $diffinfo->{'to_file'}) {
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
format_diff_cc_simplified($diffinfo, @hash_parents) .
"</div>\n"; # class="patch"
$patch_idx++;
$patch_number++;
+
+ last if $patch_idx > $#$difftree;
+ $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
}
- } until (!defined $to_name || $to_name eq $diffinfo->{'to_file'} ||
- $patch_idx > $#$difftree);
+ }
# modifies %from, %to hashes
parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents);
@@ -3210,30 +3208,36 @@ sub git_patchset_body {
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
}
+ # git diff header
+ #assert($patch_line =~ m/^diff /) if DEBUG;
+ #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
+ $patch_number++;
# print "git diff" header
- $patch_line = shift @diff_header;
print format_git_diff_header_line($patch_line, $diffinfo,
\%from, \%to);
# print extended diff header
- print "<div class=\"diff extended_header\">\n" if (@diff_header > 0);
+ print "<div class=\"diff extended_header\">\n";
EXTENDED_HEADER:
- foreach $patch_line (@diff_header) {
+ while ($patch_line = <$fd>) {
+ chomp $patch_line;
+
+ last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
+
print format_extended_diff_header_line($patch_line, $diffinfo,
\%from, \%to);
}
- print "</div>\n" if (@diff_header > 0); # class="diff extended_header"
+ print "</div>\n"; # class="diff extended_header"
# from-file/to-file diff header
- $patch_line = $last_patch_line;
if (! $patch_line) {
print "</div>\n"; # class="patch"
last PATCH;
}
next PATCH if ($patch_line =~ m/^diff /);
#assert($patch_line =~ m/^---/) if DEBUG;
- #assert($patch_line eq $last_patch_line) if DEBUG;
+ my $last_patch_line = $patch_line;
$patch_line = <$fd>;
chomp $patch_line;
#assert($patch_line =~ m/^\+\+\+/) if DEBUG;
@@ -3258,16 +3262,11 @@ sub git_patchset_body {
# for compact combined (--cc) format, with chunk and patch simpliciaction
# patchset might be empty, but there might be unprocessed raw lines
- for ($patch_idx++ if $patch_number > 0;
+ for (++$patch_idx if $patch_number > 0;
$patch_idx < @$difftree;
- $patch_idx++) {
+ ++$patch_idx) {
# read and prepare patch information
- if (ref($difftree->[$patch_idx]) eq "HASH") {
- # pre-parsed (or generated by hand)
- $diffinfo = $difftree->[$patch_idx];
- } else {
- $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
- }
+ $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
# generate anchor for "patch" links in difftree / whatchanged part
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
@@ -3395,7 +3394,7 @@ sub git_project_list_body {
"<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-class => "list", -title => $pr->{'descr_long'}},
esc_html($pr->{'descr'})) . "</td>\n" .
- "<td><i>" . esc_html(chop_str($pr->{'owner'}, 15)) . "</i></td>\n";
+ "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
print "<td class=\"". age_class($pr->{'age'}) . "\">" .
(defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
"<td class=\"link\">" .
@@ -3437,9 +3436,10 @@ sub git_shortlog_body {
print "<tr class=\"light\">\n";
}
$alternate ^= 1;
+ my $author = chop_and_escape_str($co{'author_name'}, 10);
# git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
+ "<td><i>" . $author . "</i></td>\n" .
"<td>";
print format_subject_html($co{'title'}, $co{'title_short'},
href(action=>"commit", hash=>$commit), $ref);
@@ -3487,9 +3487,10 @@ sub git_history_body {
print "<tr class=\"light\">\n";
}
$alternate ^= 1;
+ # shortlog uses chop_str($co{'author_name'}, 10)
+ my $author = chop_and_escape_str($co{'author_name'}, 15, 3);
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- # shortlog uses chop_str($co{'author_name'}, 10)
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
+ "<td><i>" . $author . "</i></td>\n" .
"<td>";
# originally git_history used chop_str($co{'title'}, 50)
print format_subject_html($co{'title'}, $co{'title_short'},
@@ -3643,11 +3644,12 @@ sub git_search_grep_body {
print "<tr class=\"light\">\n";
}
$alternate ^= 1;
+ my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
+ "<td><i>" . $author . "</i></td>\n" .
"<td>" .
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
- esc_html(chop_str($co{'title'}, 50)) . "<br/>");
+ chop_and_escape_str($co{'title'}, 50) . "<br/>");
my $comment = $co{'comment'};
foreach my $line (@$comment) {
if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
@@ -5157,12 +5159,13 @@ sub git_search {
print "<tr class=\"light\">\n";
}
$alternate ^= 1;
+ my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
+ "<td><i>" . $author . "</i></td>\n" .
"<td>" .
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
-class => "list subject"},
- esc_html(chop_str($co{'title'}, 50)) . "<br/>");
+ chop_and_escape_str($co{'title'}, 50) . "<br/>");
while (my $setref = shift @files) {
my %set = %$setref;
print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
diff --git a/hash.c b/hash.c
new file mode 100644
index 0000000..7b492d4
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,110 @@
+/*
+ * Some generic hashing helpers.
+ */
+#include "cache.h"
+#include "hash.h"
+
+/*
+ * Look up a hash entry in the hash table. Return the pointer to
+ * the existing entry, or the empty slot if none existed. The caller
+ * can then look at the (*ptr) to see whether it existed or not.
+ */
+static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table)
+{
+ unsigned int size = table->size, nr = hash % size;
+ struct hash_table_entry *array = table->array;
+
+ while (array[nr].ptr) {
+ if (array[nr].hash == hash)
+ break;
+ nr++;
+ if (nr >= size)
+ nr = 0;
+ }
+ return array + nr;
+}
+
+
+/*
+ * Insert a new hash entry pointer into the table.
+ *
+ * If that hash entry already existed, return the pointer to
+ * the existing entry (and the caller can create a list of the
+ * pointers or do anything else). If it didn't exist, return
+ * NULL (and the caller knows the pointer has been inserted).
+ */
+static void **insert_hash_entry(unsigned int hash, void *ptr, struct hash_table *table)
+{
+ struct hash_table_entry *entry = lookup_hash_entry(hash, table);
+
+ if (!entry->ptr) {
+ entry->ptr = ptr;
+ entry->hash = hash;
+ table->nr++;
+ return NULL;
+ }
+ return &entry->ptr;
+}
+
+static void grow_hash_table(struct hash_table *table)
+{
+ unsigned int i;
+ unsigned int old_size = table->size, new_size;
+ struct hash_table_entry *old_array = table->array, *new_array;
+
+ new_size = alloc_nr(old_size);
+ new_array = xcalloc(sizeof(struct hash_table_entry), new_size);
+ table->size = new_size;
+ table->array = new_array;
+ table->nr = 0;
+ for (i = 0; i < old_size; i++) {
+ unsigned int hash = old_array[i].hash;
+ void *ptr = old_array[i].ptr;
+ if (ptr)
+ insert_hash_entry(hash, ptr, table);
+ }
+ free(old_array);
+}
+
+void *lookup_hash(unsigned int hash, struct hash_table *table)
+{
+ if (!table->array)
+ return NULL;
+ return &lookup_hash_entry(hash, table)->ptr;
+}
+
+void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
+{
+ unsigned int nr = table->nr;
+ if (nr >= table->size/2)
+ grow_hash_table(table);
+ return insert_hash_entry(hash, ptr, table);
+}
+
+int for_each_hash(struct hash_table *table, int (*fn)(void *))
+{
+ int sum = 0;
+ unsigned int i;
+ unsigned int size = table->size;
+ struct hash_table_entry *array = table->array;
+
+ for (i = 0; i < size; i++) {
+ void *ptr = array->ptr;
+ array++;
+ if (ptr) {
+ int val = fn(ptr);
+ if (val < 0)
+ return val;
+ sum += val;
+ }
+ }
+ return sum;
+}
+
+void free_hash(struct hash_table *table)
+{
+ free(table->array);
+ table->array = NULL;
+ table->size = 0;
+ table->nr = 0;
+}
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..a8b0fbb
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,43 @@
+#ifndef HASH_H
+#define HASH_H
+
+/*
+ * These are some simple generic hash table helper functions.
+ * Not necessarily suitable for all users, but good for things
+ * where you want to just keep track of a list of things, and
+ * have a good hash to use on them.
+ *
+ * It keeps the hash table at roughly 50-75% free, so the memory
+ * cost of the hash table itself is roughly
+ *
+ * 3 * 2*sizeof(void *) * nr_of_objects
+ *
+ * bytes.
+ *
+ * FIXME: on 64-bit architectures, we waste memory. It would be
+ * good to have just 32-bit pointers, requiring a special allocator
+ * for hashed entries or something.
+ */
+struct hash_table_entry {
+ unsigned int hash;
+ void *ptr;
+};
+
+struct hash_table {
+ unsigned int size, nr;
+ struct hash_table_entry *array;
+};
+
+extern void *lookup_hash(unsigned int hash, struct hash_table *table);
+extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
+extern int for_each_hash(struct hash_table *table, int (*fn)(void *));
+extern void free_hash(struct hash_table *table);
+
+static inline void init_hash(struct hash_table *table)
+{
+ table->size = 0;
+ table->nr = 0;
+ table->array = NULL;
+}
+
+#endif
diff --git a/help.c b/help.c
index e5662d9..855aeef 100644
--- a/help.c
+++ b/help.c
@@ -37,24 +37,25 @@ static inline void mput_char(char c, unsigned int num)
putchar(c);
}
-static struct cmdname {
- size_t len;
- char name[1];
-} **cmdname;
-static int cmdname_alloc, cmdname_cnt;
-
-static void add_cmdname(const char *name, int len)
+static struct cmdnames {
+ int alloc;
+ int cnt;
+ struct cmdname {
+ size_t len;
+ char name[1];
+ } **names;
+} main_cmds, other_cmds;
+
+static void add_cmdname(struct cmdnames *cmds, const char *name, int len)
{
- struct cmdname *ent;
- if (cmdname_alloc <= cmdname_cnt) {
- cmdname_alloc = cmdname_alloc + 200;
- cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname));
- }
- ent = xmalloc(sizeof(*ent) + len);
+ struct cmdname *ent = xmalloc(sizeof(*ent) + len);
+
ent->len = len;
memcpy(ent->name, name, len);
ent->name[len] = 0;
- cmdname[cmdname_cnt++] = ent;
+
+ ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
+ cmds->names[cmds->cnt++] = ent;
}
static int cmdname_compare(const void *a_, const void *b_)
@@ -64,7 +65,42 @@ static int cmdname_compare(const void *a_, const void *b_)
return strcmp(a->name, b->name);
}
-static void pretty_print_string_list(struct cmdname **cmdname, int longest)
+static void uniq(struct cmdnames *cmds)
+{
+ int i, j;
+
+ if (!cmds->cnt)
+ return;
+
+ for (i = j = 1; i < cmds->cnt; i++)
+ if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
+ cmds->names[j++] = cmds->names[i];
+
+ cmds->cnt = j;
+}
+
+static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) {
+ int ci, cj, ei;
+ int cmp;
+
+ ci = cj = ei = 0;
+ while (ci < cmds->cnt && ei < excludes->cnt) {
+ cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
+ if (cmp < 0)
+ cmds->names[cj++] = cmds->names[ci++];
+ else if (cmp == 0)
+ ci++, ei++;
+ else if (cmp > 0)
+ ei++;
+ }
+
+ while (ci < cmds->cnt)
+ cmds->names[cj++] = cmds->names[ci++];
+
+ cmds->cnt = cj;
+}
+
+static void pretty_print_string_list(struct cmdnames *cmds, int longest)
{
int cols = 1, rows;
int space = longest + 1; /* min 1 SP between words */
@@ -73,9 +109,7 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest)
if (space < max_cols)
cols = max_cols / space;
- rows = (cmdname_cnt + cols - 1) / cols;
-
- qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
+ rows = (cmds->cnt + cols - 1) / cols;
for (i = 0; i < rows; i++) {
printf(" ");
@@ -83,71 +117,112 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest)
for (j = 0; j < cols; j++) {
int n = j * rows + i;
int size = space;
- if (n >= cmdname_cnt)
+ if (n >= cmds->cnt)
break;
- if (j == cols-1 || n + rows >= cmdname_cnt)
+ if (j == cols-1 || n + rows >= cmds->cnt)
size = 1;
- printf("%-*s", size, cmdname[n]->name);
+ printf("%-*s", size, cmds->names[n]->name);
}
putchar('\n');
}
}
-static void list_commands(const char *exec_path, const char *pattern)
+static unsigned int list_commands_in_dir(struct cmdnames *cmds,
+ const char *path)
{
unsigned int longest = 0;
- char path[PATH_MAX];
- int dirlen;
- DIR *dir = opendir(exec_path);
+ const char *prefix = "git-";
+ int prefix_len = strlen(prefix);
+ DIR *dir = opendir(path);
struct dirent *de;
- if (!dir) {
- fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
- exit(1);
- }
-
- dirlen = strlen(exec_path);
- if (PATH_MAX - 20 < dirlen) {
- fprintf(stderr, "git: insanely long exec-path '%s'\n",
- exec_path);
- exit(1);
- }
-
- memcpy(path, exec_path, dirlen);
- path[dirlen++] = '/';
+ if (!dir || chdir(path))
+ return 0;
while ((de = readdir(dir)) != NULL) {
struct stat st;
int entlen;
- if (prefixcmp(de->d_name, "git-"))
+ if (prefixcmp(de->d_name, prefix))
continue;
- strcpy(path+dirlen, de->d_name);
- if (stat(path, &st) || /* stat, not lstat */
+
+ if (stat(de->d_name, &st) || /* stat, not lstat */
!S_ISREG(st.st_mode) ||
!(st.st_mode & S_IXUSR))
continue;
- entlen = strlen(de->d_name);
+ entlen = strlen(de->d_name) - prefix_len;
if (has_extension(de->d_name, ".exe"))
entlen -= 4;
if (longest < entlen)
longest = entlen;
- add_cmdname(de->d_name + 4, entlen-4);
+ add_cmdname(cmds, de->d_name + prefix_len, entlen);
}
closedir(dir);
- printf("git commands available in '%s'\n", exec_path);
- printf("----------------------------");
- mput_char('-', strlen(exec_path));
- putchar('\n');
- pretty_print_string_list(cmdname, longest - 4);
- putchar('\n');
+ return longest;
}
-static void list_common_cmds_help(void)
+static void list_commands(void)
+{
+ unsigned int longest = 0;
+ unsigned int len;
+ const char *env_path = getenv("PATH");
+ char *paths, *path, *colon;
+ const char *exec_path = git_exec_path();
+
+ if (exec_path)
+ longest = list_commands_in_dir(&main_cmds, exec_path);
+
+ if (!env_path) {
+ fprintf(stderr, "PATH not set\n");
+ exit(1);
+ }
+
+ path = paths = xstrdup(env_path);
+ while (1) {
+ if ((colon = strchr(path, ':')))
+ *colon = 0;
+
+ len = list_commands_in_dir(&other_cmds, path);
+ if (len > longest)
+ longest = len;
+
+ if (!colon)
+ break;
+ path = colon + 1;
+ }
+ free(paths);
+
+ qsort(main_cmds.names, main_cmds.cnt,
+ sizeof(*main_cmds.names), cmdname_compare);
+ uniq(&main_cmds);
+
+ qsort(other_cmds.names, other_cmds.cnt,
+ sizeof(*other_cmds.names), cmdname_compare);
+ uniq(&other_cmds);
+ exclude_cmds(&other_cmds, &main_cmds);
+
+ if (main_cmds.cnt) {
+ printf("available git commands in '%s'\n", exec_path);
+ printf("----------------------------");
+ mput_char('-', strlen(exec_path));
+ putchar('\n');
+ pretty_print_string_list(&main_cmds, longest);
+ putchar('\n');
+ }
+
+ if (other_cmds.cnt) {
+ printf("git commands available from elsewhere on your $PATH\n");
+ printf("---------------------------------------------------\n");
+ pretty_print_string_list(&other_cmds, longest);
+ putchar('\n');
+ }
+}
+
+void list_common_cmds_help(void)
{
int i, longest = 0;
@@ -198,19 +273,17 @@ int cmd_version(int argc, const char **argv, const char *prefix)
int cmd_help(int argc, const char **argv, const char *prefix)
{
const char *help_cmd = argc > 1 ? argv[1] : NULL;
- const char *exec_path = git_exec_path();
if (!help_cmd) {
printf("usage: %s\n\n", git_usage_string);
list_common_cmds_help();
- exit(1);
+ exit(0);
}
else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
printf("usage: %s\n\n", git_usage_string);
- if(exec_path)
- list_commands(exec_path, "git-*");
- exit(1);
+ list_commands();
+ exit(0);
}
else
diff --git a/index-pack.c b/index-pack.c
index db58e05..61ea762 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -46,7 +46,7 @@ static int nr_resolved_deltas;
static int from_stdin;
static int verbose;
-static struct progress progress;
+static struct progress *progress;
/* We always read in 4kB chunks. */
static unsigned char input_buffer[4096];
@@ -87,6 +87,8 @@ static void *fill(int min)
die("early EOF");
die("read error on input: %s", strerror(errno));
}
+ if (from_stdin)
+ display_throughput(progress, ret);
input_len += ret;
} while (input_len < min);
return input_buffer;
@@ -106,7 +108,7 @@ static void use(int bytes)
consumed_bytes += bytes;
}
-static const char *open_pack_file(const char *pack_name)
+static char *open_pack_file(char *pack_name)
{
if (from_stdin) {
input_fd = 0;
@@ -406,7 +408,9 @@ static void parse_pack_objects(unsigned char *sha1)
* - remember base (SHA1 or offset) for all deltas.
*/
if (verbose)
- start_progress(&progress, "Indexing %u objects...", "", nr_objects);
+ progress = start_progress(
+ from_stdin ? "Receiving objects" : "Indexing objects",
+ nr_objects);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
data = unpack_raw_entry(obj, &delta->base);
@@ -418,12 +422,10 @@ static void parse_pack_objects(unsigned char *sha1)
} else
sha1_object(data, obj->size, obj->type, obj->idx.sha1);
free(data);
- if (verbose)
- display_progress(&progress, i+1);
+ display_progress(progress, i+1);
}
objects[i].idx.offset = consumed_bytes;
- if (verbose)
- stop_progress(&progress);
+ stop_progress(&progress);
/* Check pack integrity */
flush();
@@ -455,7 +457,7 @@ static void parse_pack_objects(unsigned char *sha1)
* for some more deltas.
*/
if (verbose)
- start_progress(&progress, "Resolving %u deltas...", "", nr_deltas);
+ progress = start_progress("Resolving deltas", nr_deltas);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
union delta_base base;
@@ -486,8 +488,7 @@ static void parse_pack_objects(unsigned char *sha1)
obj->size, obj->type);
}
free(data);
- if (verbose)
- display_progress(&progress, nr_resolved_deltas);
+ display_progress(progress, nr_resolved_deltas);
}
}
@@ -594,8 +595,7 @@ static void fix_unresolved_deltas(int nr_unresolved)
die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
append_obj_to_pack(d->base.sha1, data, size, type);
free(data);
- if (verbose)
- display_progress(&progress, nr_resolved_deltas);
+ display_progress(progress, nr_resolved_deltas);
}
free(sorted_by_pos);
}
@@ -686,15 +686,15 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
int main(int argc, char **argv)
{
int i, fix_thin_pack = 0;
- const char *curr_pack, *pack_name = NULL;
- const char *curr_index, *index_name = NULL;
+ char *curr_pack, *pack_name = NULL;
+ char *curr_index, *index_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
char *index_name_buf = NULL, *keep_name_buf = NULL;
struct pack_idx_entry **idx_objects;
unsigned char sha1[20];
for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ char *arg = argv[i];
if (*arg == '-') {
if (!strcmp(arg, "--stdin")) {
@@ -774,8 +774,7 @@ int main(int argc, char **argv)
deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
parse_pack_objects(sha1);
if (nr_deltas == nr_resolved_deltas) {
- if (verbose)
- stop_progress(&progress);
+ stop_progress(&progress);
/* Flush remaining pack final 20-byte SHA1. */
flush();
} else {
@@ -788,11 +787,10 @@ int main(int argc, char **argv)
(nr_objects + nr_unresolved + 1)
* sizeof(*objects));
fix_unresolved_deltas(nr_unresolved);
- if (verbose) {
- stop_progress(&progress);
+ stop_progress(&progress);
+ if (verbose)
fprintf(stderr, "%d objects were added to complete this thin pack.\n",
nr_objects - nr_objects_initial);
- }
fixup_pack_header_footer(output_fd, sha1,
curr_pack, nr_objects);
}
@@ -815,6 +813,10 @@ int main(int argc, char **argv)
free(objects);
free(index_name_buf);
free(keep_name_buf);
+ if (pack_name == NULL)
+ free(curr_pack);
+ if (index_name == NULL)
+ free(curr_index);
return 0;
}
diff --git a/log-tree.c b/log-tree.c
index 62edd34..3763ce9 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -15,7 +15,7 @@ static void show_parents(struct commit *commit, int abbrev)
}
}
-static void show_decorations(struct commit *commit)
+void show_decorations(struct commit *commit)
{
const char *prefix;
struct name_decoration *decoration;
diff --git a/log-tree.h b/log-tree.h
index e82b56a..b33f7cd 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -12,5 +12,6 @@ int log_tree_diff_flush(struct rev_info *);
int log_tree_commit(struct rev_info *, struct commit *);
int log_tree_opt_parse(struct rev_info *, const char **, int);
void show_log(struct rev_info *opt, const char *sep);
+void show_decorations(struct commit *commit);
#endif
diff --git a/pack-write.c b/pack-write.c
index 979bdff..665e2b2 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -17,7 +17,8 @@ static int sha1_compare(const void *_a, const void *_b)
* the SHA1 hash of sorted object names. The objects array passed in
* will be sorted by SHA1 on exit.
*/
-const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1)
+char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
+ int nr_objects, unsigned char *sha1)
{
struct sha1file *f;
struct pack_idx_entry **sorted_by_sha, **list, **last;
diff --git a/pack.h b/pack.h
index b57ba2d..b31b376 100644
--- a/pack.h
+++ b/pack.h
@@ -55,7 +55,7 @@ struct pack_idx_entry {
off_t offset;
};
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
+extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
extern int verify_pack(struct packed_git *, int);
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
diff --git a/peek-remote.c b/peek-remote.c
index ceb7871..8d20f7c 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -25,7 +25,7 @@ int main(int argc, char **argv)
int i, ret;
char *dest = NULL;
int fd[2];
- pid_t pid;
+ struct child_process *conn;
int nongit = 0;
unsigned flags = 0;
@@ -64,12 +64,10 @@ int main(int argc, char **argv)
if (!dest || i != argc - 1)
usage(peek_remote_usage);
- pid = git_connect(fd, dest, uploadpack, 0);
- if (pid < 0)
- return 1;
+ conn = git_connect(fd, dest, uploadpack, 0);
ret = peek_remote(fd, flags);
close(fd[0]);
close(fd[1]);
- ret |= finish_connect(pid);
+ ret |= finish_connect(conn);
return !!ret;
}
diff --git a/progress.c b/progress.c
index 4344f4e..3f6a602 100644
--- a/progress.c
+++ b/progress.c
@@ -1,6 +1,40 @@
+/*
+ * Simple text-based progress display module for GIT
+ *
+ * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
#include "git-compat-util.h"
#include "progress.h"
+#define TP_IDX_MAX 8
+
+struct throughput {
+ struct timeval prev_tv;
+ off_t total;
+ unsigned long count;
+ unsigned long avg_bytes;
+ unsigned long last_bytes[TP_IDX_MAX];
+ unsigned int avg_misecs;
+ unsigned int last_misecs[TP_IDX_MAX];
+ unsigned int idx;
+ char display[32];
+};
+
+struct progress {
+ const char *title;
+ int last_value;
+ unsigned total;
+ unsigned last_percent;
+ unsigned delay;
+ unsigned delayed_percent_treshold;
+ struct throughput *throughput;
+};
+
static volatile sig_atomic_t progress_update;
static void progress_interval(int signum)
@@ -35,10 +69,11 @@ static void clear_progress_signal(void)
progress_update = 0;
}
-int display_progress(struct progress *progress, unsigned n)
+static int display(struct progress *progress, unsigned n, int done)
{
+ char *eol, *tp;
+
if (progress->delay) {
- char buf[80];
if (!progress_update || --progress->delay)
return 0;
if (progress->total) {
@@ -51,60 +86,151 @@ int display_progress(struct progress *progress, unsigned n)
return 0;
}
}
- if (snprintf(buf, sizeof(buf),
- progress->delayed_title, progress->total))
- fprintf(stderr, "%s\n", buf);
}
+
+ progress->last_value = n;
+ tp = (progress->throughput) ? progress->throughput->display : "";
+ eol = done ? ", done. \n" : " \r";
if (progress->total) {
unsigned percent = n * 100 / progress->total;
if (percent != progress->last_percent || progress_update) {
progress->last_percent = percent;
- fprintf(stderr, "%s%4u%% (%u/%u) done\r",
- progress->prefix, percent, n, progress->total);
+ fprintf(stderr, "%s: %3u%% (%u/%u)%s%s",
+ progress->title, percent, n,
+ progress->total, tp, eol);
progress_update = 0;
- progress->need_lf = 1;
return 1;
}
} else if (progress_update) {
- fprintf(stderr, "%s%u\r", progress->prefix, n);
+ fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol);
progress_update = 0;
- progress->need_lf = 1;
return 1;
}
+
return 0;
}
-void start_progress(struct progress *progress, const char *title,
- const char *prefix, unsigned total)
+void display_throughput(struct progress *progress, unsigned long n)
{
- char buf[80];
- progress->prefix = prefix;
- progress->total = total;
- progress->last_percent = -1;
- progress->delay = 0;
- progress->need_lf = 0;
- if (snprintf(buf, sizeof(buf), title, total))
- fprintf(stderr, "%s\n", buf);
- set_progress_signal();
+ struct throughput *tp;
+ struct timeval tv;
+ unsigned int misecs;
+
+ if (!progress)
+ return;
+ tp = progress->throughput;
+
+ gettimeofday(&tv, NULL);
+
+ if (!tp) {
+ progress->throughput = tp = calloc(1, sizeof(*tp));
+ if (tp)
+ tp->prev_tv = tv;
+ return;
+ }
+
+ tp->total += n;
+ tp->count += n;
+
+ /*
+ * We have x = bytes and y = microsecs. We want z = KiB/s:
+ *
+ * z = (x / 1024) / (y / 1000000)
+ * z = x / y * 1000000 / 1024
+ * z = x / (y * 1024 / 1000000)
+ * z = x / y'
+ *
+ * To simplify things we'll keep track of misecs, or 1024th of a sec
+ * obtained with:
+ *
+ * y' = y * 1024 / 1000000
+ * y' = y / (1000000 / 1024)
+ * y' = y / 977
+ */
+ misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024;
+ misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
+
+ if (misecs > 512) {
+ int l = sizeof(tp->display);
+ tp->prev_tv = tv;
+ tp->avg_bytes += tp->count;
+ tp->avg_misecs += misecs;
+
+ if (tp->total > 1 << 30) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u GiB",
+ (int)(tp->total >> 30),
+ (int)(tp->total & ((1 << 30) - 1)) / 10737419);
+ } else if (tp->total > 1 << 20) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
+ (int)(tp->total >> 20),
+ ((int)(tp->total & ((1 << 20) - 1))
+ * 100) >> 20);
+ } else if (tp->total > 1 << 10) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
+ (int)(tp->total >> 10),
+ ((int)(tp->total & ((1 << 10) - 1))
+ * 100) >> 10);
+ } else {
+ l -= snprintf(tp->display, l, ", %u bytes",
+ (int)tp->total);
+ }
+ snprintf(tp->display + sizeof(tp->display) - l, l,
+ " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs);
+
+ tp->avg_bytes -= tp->last_bytes[tp->idx];
+ tp->avg_misecs -= tp->last_misecs[tp->idx];
+ tp->last_bytes[tp->idx] = tp->count;
+ tp->last_misecs[tp->idx] = misecs;
+ tp->idx = (tp->idx + 1) % TP_IDX_MAX;
+ tp->count = 0;
+
+ if (progress->last_value != -1 && progress_update)
+ display(progress, progress->last_value, 0);
+ }
+}
+
+int display_progress(struct progress *progress, unsigned n)
+{
+ return progress ? display(progress, n, 0) : 0;
}
-void start_progress_delay(struct progress *progress, const char *title,
- const char *prefix, unsigned total,
- unsigned percent_treshold, unsigned delay)
+struct progress *start_progress_delay(const char *title, unsigned total,
+ unsigned percent_treshold, unsigned delay)
{
- progress->prefix = prefix;
+ struct progress *progress = malloc(sizeof(*progress));
+ if (!progress) {
+ /* unlikely, but here's a good fallback */
+ fprintf(stderr, "%s...\n", title);
+ return NULL;
+ }
+ progress->title = title;
progress->total = total;
+ progress->last_value = -1;
progress->last_percent = -1;
progress->delayed_percent_treshold = percent_treshold;
- progress->delayed_title = title;
progress->delay = delay;
- progress->need_lf = 0;
+ progress->throughput = NULL;
set_progress_signal();
+ return progress;
}
-void stop_progress(struct progress *progress)
+struct progress *start_progress(const char *title, unsigned total)
{
+ return start_progress_delay(title, total, 0, 0);
+}
+
+void stop_progress(struct progress **p_progress)
+{
+ struct progress *progress = *p_progress;
+ if (!progress)
+ return;
+ *p_progress = NULL;
+ if (progress->last_value != -1) {
+ /* Force the last update */
+ progress_update = 1;
+ display(progress, progress->last_value, 1);
+ }
clear_progress_signal();
- if (progress->need_lf)
- fputc('\n', stderr);
+ free(progress->throughput);
+ free(progress);
}
diff --git a/progress.h b/progress.h
index a7c17ca..61cb68d 100644
--- a/progress.h
+++ b/progress.h
@@ -1,22 +1,13 @@
#ifndef PROGRESS_H
#define PROGRESS_H
-struct progress {
- const char *prefix;
- unsigned total;
- unsigned last_percent;
- unsigned delay;
- unsigned delayed_percent_treshold;
- const char *delayed_title;
- int need_lf;
-};
+struct progress;
+void display_throughput(struct progress *progress, unsigned long n);
int display_progress(struct progress *progress, unsigned n);
-void start_progress(struct progress *progress, const char *title,
- const char *prefix, unsigned total);
-void start_progress_delay(struct progress *progress, const char *title,
- const char *prefix, unsigned total,
- unsigned percent_treshold, unsigned delay);
-void stop_progress(struct progress *progress);
+struct progress *start_progress(const char *title, unsigned total);
+struct progress *start_progress_delay(const char *title, unsigned total,
+ unsigned percent_treshold, unsigned delay);
+void stop_progress(struct progress **progress);
#endif
diff --git a/run-command.c b/run-command.c
index 7e779d3..d99a6c4 100644
--- a/run-command.c
+++ b/run-command.c
@@ -17,8 +17,8 @@ static inline void dup_devnull(int to)
int start_command(struct child_process *cmd)
{
- int need_in, need_out;
- int fdin[2], fdout[2];
+ int need_in, need_out, need_err;
+ int fdin[2], fdout[2], fderr[2];
need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
@@ -41,12 +41,26 @@ int start_command(struct child_process *cmd)
cmd->close_out = 1;
}
+ need_err = cmd->err < 0;
+ if (need_err) {
+ if (pipe(fderr) < 0) {
+ if (need_in)
+ close_pair(fdin);
+ if (need_out)
+ close_pair(fdout);
+ return -ERR_RUN_COMMAND_PIPE;
+ }
+ cmd->err = fderr[0];
+ }
+
cmd->pid = fork();
if (cmd->pid < 0) {
if (need_in)
close_pair(fdin);
if (need_out)
close_pair(fdout);
+ if (need_err)
+ close_pair(fderr);
return -ERR_RUN_COMMAND_FORK;
}
@@ -73,6 +87,11 @@ int start_command(struct child_process *cmd)
close(cmd->out);
}
+ if (need_err) {
+ dup2(fderr[1], 2);
+ close_pair(fderr);
+ }
+
if (cmd->dir && chdir(cmd->dir))
die("exec %s: cd to %s failed (%s)", cmd->argv[0],
cmd->dir, strerror(errno));
@@ -102,19 +121,17 @@ int start_command(struct child_process *cmd)
else if (cmd->out > 1)
close(cmd->out);
+ if (need_err)
+ close(fderr[1]);
+
return 0;
}
-int finish_command(struct child_process *cmd)
+static int wait_or_whine(pid_t pid)
{
- if (cmd->close_in)
- close(cmd->in);
- if (cmd->close_out)
- close(cmd->out);
-
for (;;) {
int status, code;
- pid_t waiting = waitpid(cmd->pid, &status, 0);
+ pid_t waiting = waitpid(pid, &status, 0);
if (waiting < 0) {
if (errno == EINTR)
@@ -122,7 +139,7 @@ int finish_command(struct child_process *cmd)
error("waitpid failed (%s)", strerror(errno));
return -ERR_RUN_COMMAND_WAITPID;
}
- if (waiting != cmd->pid)
+ if (waiting != pid)
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
if (WIFSIGNALED(status))
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
@@ -136,6 +153,15 @@ int finish_command(struct child_process *cmd)
}
}
+int finish_command(struct child_process *cmd)
+{
+ if (cmd->close_in)
+ close(cmd->in);
+ if (cmd->close_out)
+ close(cmd->out);
+ return wait_or_whine(cmd->pid);
+}
+
int run_command(struct child_process *cmd)
{
int code = start_command(cmd);
@@ -178,3 +204,34 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
cmd.env = env;
return run_command(&cmd);
}
+
+int start_async(struct async *async)
+{
+ int pipe_out[2];
+
+ if (pipe(pipe_out) < 0)
+ return error("cannot create pipe: %s", strerror(errno));
+
+ async->pid = fork();
+ if (async->pid < 0) {
+ error("fork (async) failed: %s", strerror(errno));
+ close_pair(pipe_out);
+ return -1;
+ }
+ if (!async->pid) {
+ close(pipe_out[0]);
+ exit(!!async->proc(pipe_out[1], async->data));
+ }
+ async->out = pipe_out[0];
+ close(pipe_out[1]);
+ return 0;
+}
+
+int finish_async(struct async *async)
+{
+ int ret = 0;
+
+ if (wait_or_whine(async->pid))
+ ret = error("waitpid (async) failed");
+ return ret;
+}
diff --git a/run-command.h b/run-command.h
index 7958eb1..94e1e9d 100644
--- a/run-command.h
+++ b/run-command.h
@@ -16,6 +16,7 @@ struct child_process {
pid_t pid;
int in;
int out;
+ int err;
const char *dir;
const char *const *env;
unsigned close_in:1;
@@ -42,4 +43,26 @@ int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
*/
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
+/*
+ * The purpose of the following functions is to feed a pipe by running
+ * a function asynchronously and providing output that the caller reads.
+ *
+ * It is expected that no synchronization and mutual exclusion between
+ * the caller and the feed function is necessary so that the function
+ * can run in a thread without interfering with the caller.
+ */
+struct async {
+ /*
+ * proc writes to fd and closes it;
+ * returns 0 on success, non-zero on failure
+ */
+ int (*proc)(int fd, void *data);
+ void *data;
+ int out; /* caller reads from here and closes it */
+ pid_t pid;
+};
+
+int start_async(struct async *async);
+int finish_async(struct async *async);
+
#endif
diff --git a/send-pack.c b/send-pack.c
index e9b9a39..5e127a1 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -178,6 +178,35 @@ static int receive_status(int in)
return ret;
}
+static void update_tracking_ref(struct remote *remote, struct ref *ref)
+{
+ struct refspec rs;
+ int will_delete_ref;
+
+ rs.src = ref->name;
+ rs.dst = NULL;
+
+ if (!ref->peer_ref)
+ return;
+
+ will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
+
+ if (!will_delete_ref &&
+ !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
+ return;
+
+ if (!remote_find_tracking(remote, &rs)) {
+ fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+ if (is_null_sha1(ref->peer_ref->new_sha1)) {
+ if (delete_ref(rs.dst, NULL))
+ error("Failed to delete");
+ } else
+ update_ref("update by push", rs.dst,
+ ref->new_sha1, NULL, 0, 0);
+ free(rs.dst);
+ }
+}
+
static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
{
struct ref *ref;
@@ -306,22 +335,6 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
fprintf(stderr, "\n from %s\n to %s\n",
old_hex, new_hex);
}
- if (remote && !dry_run) {
- struct refspec rs;
- rs.src = ref->name;
- rs.dst = NULL;
- if (!remote_find_tracking(remote, &rs)) {
- fprintf(stderr, " Also local %s\n", rs.dst);
- if (will_delete_ref) {
- if (delete_ref(rs.dst, NULL)) {
- error("Failed to delete");
- }
- } else
- update_ref("update by push", rs.dst,
- ref->new_sha1, NULL, 0, 0);
- free(rs.dst);
- }
- }
}
packet_flush(out);
@@ -334,6 +347,11 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
ret = -4;
}
+ if (!dry_run && remote && ret == 0) {
+ for (ref = remote_refs; ref; ref = ref->next)
+ update_tracking_ref(remote, ref);
+ }
+
if (!new_refs && ret == 0)
fprintf(stderr, "Everything up-to-date\n");
return ret;
@@ -366,7 +384,7 @@ int main(int argc, char **argv)
char *dest = NULL;
char **heads = NULL;
int fd[2], ret;
- pid_t pid;
+ struct child_process *conn;
char *remote_name = NULL;
struct remote *remote = NULL;
@@ -434,12 +452,10 @@ int main(int argc, char **argv)
}
}
- pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
- if (pid < 0)
- return 1;
+ conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
close(fd[0]);
close(fd[1]);
- ret |= finish_connect(pid);
+ ret |= finish_connect(conn);
return !!ret;
}
diff --git a/shell.c b/shell.c
index cfe372b..9826109 100644
--- a/shell.c
+++ b/shell.c
@@ -24,17 +24,11 @@ static int do_cvs_cmd(const char *me, char *arg)
const char *cvsserver_argv[3] = {
"cvsserver", "server", NULL
};
- const char *oldpath = getenv("PATH");
- struct strbuf newpath = STRBUF_INIT;
if (!arg || strcmp(arg, "server"))
die("git-cvsserver only handles server: %s", arg);
- strbuf_addstr(&newpath, git_exec_path());
- strbuf_addch(&newpath, ':');
- strbuf_addstr(&newpath, oldpath);
-
- setenv("PATH", strbuf_detach(&newpath, NULL), 1);
+ setup_path(NULL);
return execv_git_cmd(cvsserver_argv);
}
diff --git a/sideband.c b/sideband.c
index 277fa3c..ab8a1e9 100644
--- a/sideband.c
+++ b/sideband.c
@@ -17,7 +17,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
strcpy(buf, "remote:");
while (1) {
int band, len;
- len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
+ len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
if (len == 0)
break;
if (len < 1) {
@@ -35,7 +35,22 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
return SIDEBAND_REMOTE_ERROR;
case 2:
buf[7] = ' ';
- safe_write(err, buf, 8+len);
+ len += 8;
+ while (1) {
+ int brk = 8;
+ while (brk < len) {
+ brk++;
+ if (buf[brk-1] == '\n' ||
+ buf[brk-1] == '\r')
+ break;
+ }
+ safe_write(err, buf, brk);
+ if (brk < len) {
+ memmove(buf + 8, buf + brk, len - brk);
+ len = len - brk + 8;
+ } else
+ break;
+ }
continue;
case 1:
safe_write(out, buf+8, len);
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index a839f4e..cb86029 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -42,7 +42,12 @@ test_expect_success check '
git diff --raw --exit-code :test :test.i &&
id=$(git rev-parse --verify :test) &&
embedded=$(sed -ne "$script" test.i) &&
- test "z$id" = "z$embedded"
+ test "z$id" = "z$embedded" &&
+
+ git cat-file blob :test.t > test.r &&
+
+ ./rot13.sh < test.o > test.t &&
+ cmp test.r test.t
'
# If an expanded ident ever gets into the repository, we want to make sure that
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4fbd5b1..86f9b53 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -254,4 +254,32 @@ test_expect_success 'push with dry-run' '
check_push_result $old_commit heads/master
'
+test_expect_success 'push updates local refs' '
+
+ rm -rf parent child &&
+ mkdir parent && cd parent && git init &&
+ echo one >foo && git add foo && git commit -m one &&
+ cd .. &&
+ git clone parent child && cd child &&
+ echo two >foo && git commit -a -m two &&
+ git push &&
+ test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
+
+'
+
+test_expect_success 'push does not update local refs on failure' '
+
+ rm -rf parent child &&
+ mkdir parent && cd parent && git init &&
+ echo one >foo && git add foo && git commit -m one &&
+ echo exit 1 >.git/hooks/pre-receive &&
+ chmod +x .git/hooks/pre-receive &&
+ cd .. &&
+ git clone parent child && cd child &&
+ echo two >foo && git commit -a -m two || exit 1
+ git push && exit 1
+ test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
+
+'
+
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 03cdba5..53956c0 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' '
git bisect next
'
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3
+# but $HASH2 is bad,
+# so we should find $HASH2 as the first bad commit
+test_expect_success 'bisect skip: successfull result' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip &&
+ git bisect bad > my_bisect_log.txt &&
+ grep "$HASH2 is first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
+
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
+# so we should not be able to tell the first bad commit
+# among $HASH2, $HASH3 and $HASH4
+test_expect_success 'bisect skip: cannot tell between 3 commits' '
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip || return 1
+
+ if git bisect skip > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH1 my_bisect_log.txt &&
+ grep $HASH2 my_bisect_log.txt &&
+ grep $HASH3 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect reset
+ fi
+'
+
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3
+# but $HASH2 is good,
+# so we should not be able to tell the first bad commit
+# among $HASH3 and $HASH4
+test_expect_success 'bisect skip: cannot tell between 2 commits' '
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip || return 1
+
+ if git bisect good > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH1 my_bisect_log.txt &&
+ ! grep $HASH2 my_bisect_log.txt &&
+ grep $HASH3 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect reset
+ fi
+'
+
# We want to automatically find the commit that
# introduced "Another" into hello.
test_expect_success \
@@ -99,6 +156,67 @@ test_expect_success \
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
git bisect reset'
+# $HASH1 is good, $HASH5 is bad, we skip $HASH3
+# but $HASH4 is good,
+# so we should find $HASH5 as the first bad commit
+HASH5=
+test_expect_success 'bisect skip: add line and then a new test' '
+ add_line_into_file "5: Another new line." hello &&
+ HASH5=$(git rev-parse --verify HEAD) &&
+ git bisect start $HASH5 $HASH1 &&
+ git bisect skip &&
+ git bisect good > my_bisect_log.txt &&
+ grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ git bisect log > log_to_replay.txt
+ git bisect reset
+'
+
+test_expect_success 'bisect skip and bisect replay' '
+ git bisect replay log_to_replay.txt > my_bisect_log.txt &&
+ grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
+
+HASH6=
+test_expect_success 'bisect run & skip: cannot tell between 2' '
+ add_line_into_file "6: Yet a line." hello &&
+ HASH6=$(git rev-parse --verify HEAD) &&
+ echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+ echo "grep line hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH6 $HASH1 &&
+ if git bisect run ./test_script.sh > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH3 my_bisect_log.txt &&
+ ! grep $HASH6 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ grep $HASH5 my_bisect_log.txt
+ fi
+'
+
+HASH7=
+test_expect_success 'bisect run & skip: find first bad' '
+ git bisect reset &&
+ add_line_into_file "7: Should be the last line." hello &&
+ HASH7=$(git rev-parse --verify HEAD) &&
+ echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+ echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh &&
+ echo "grep Yet hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH7 $HASH1 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH6 is first bad commit" my_bisect_log.txt
+'
+
#
#
test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 714de6e..603a8cd 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -60,6 +60,7 @@ esac
# . ./test-lib.sh
[ "x$TERM" != "xdumb" ] &&
+ [ -t 1 ] &&
tput bold >/dev/null 2>&1 &&
tput setaf 1 >/dev/null 2>&1 &&
tput sgr0 >/dev/null 2>&1 &&
diff --git a/transport.c b/transport.c
index 400af71..d44fe7c 100644
--- a/transport.c
+++ b/transport.c
@@ -107,7 +107,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
return;
for (;;) {
- int cmp, len;
+ int cmp = cmp, len;
if (!fgets(buffer, sizeof(buffer), f)) {
fclose(f);
@@ -601,18 +601,13 @@ static struct ref *get_refs_via_connect(const struct transport *transport)
struct git_transport_data *data = transport->data;
struct ref *refs;
int fd[2];
- pid_t pid;
char *dest = xstrdup(transport->url);
-
- pid = git_connect(fd, dest, data->uploadpack, 0);
-
- if (pid < 0)
- die("Failed to connect to \"%s\"", transport->url);
+ struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0);
get_remote_heads(fd[0], &refs, 0, NULL, 0);
packet_flush(fd[1]);
- finish_connect(pid);
+ finish_connect(conn);
free(dest);
diff --git a/unpack-trees.c b/unpack-trees.c
index ccfeb6e..c527d7d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -297,7 +297,7 @@ static void check_updates(struct cache_entry **src, int nr,
{
unsigned short mask = htons(CE_UPDATE);
unsigned cnt = 0, total = 0;
- struct progress progress;
+ struct progress *progress = NULL;
char last_symlink[PATH_MAX];
if (o->update && o->verbose_update) {
@@ -307,8 +307,8 @@ static void check_updates(struct cache_entry **src, int nr,
total++;
}
- start_progress_delay(&progress, "Checking %u files out...",
- "", total, 50, 2);
+ progress = start_progress_delay("Checking out files",
+ total, 50, 2);
cnt = 0;
}
@@ -316,9 +316,8 @@ static void check_updates(struct cache_entry **src, int nr,
while (nr--) {
struct cache_entry *ce = *src++;
- if (total)
- if (!ce->ce_mode || ce->ce_flags & mask)
- display_progress(&progress, ++cnt);
+ if (!ce->ce_mode || ce->ce_flags & mask)
+ display_progress(progress, ++cnt);
if (!ce->ce_mode) {
if (o->update)
unlink_entry(ce->name, last_symlink);
@@ -332,8 +331,7 @@ static void check_updates(struct cache_entry **src, int nr,
}
}
}
- if (total)
- stop_progress(&progress);;
+ stop_progress(&progress);
}
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
diff --git a/upload-pack.c b/upload-pack.c
index fe96ef1..6799468 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -9,6 +9,7 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "run-command.h"
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
@@ -96,110 +97,86 @@ static void show_edge(struct commit *commit)
fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
}
+static int do_rev_list(int fd, void *create_full_pack)
+{
+ int i;
+ struct rev_info revs;
+
+ pack_pipe = fdopen(fd, "w");
+ if (create_full_pack)
+ use_thin_pack = 0; /* no point doing it */
+ init_revisions(&revs, NULL);
+ revs.tag_objects = 1;
+ revs.tree_objects = 1;
+ revs.blob_objects = 1;
+ if (use_thin_pack)
+ revs.edge_hint = 1;
+
+ if (create_full_pack) {
+ const char *args[] = {"rev-list", "--all", NULL};
+ setup_revisions(2, args, &revs, NULL);
+ } else {
+ for (i = 0; i < want_obj.nr; i++) {
+ struct object *o = want_obj.objects[i].item;
+ /* why??? */
+ o->flags &= ~UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
+ }
+ for (i = 0; i < have_obj.nr; i++) {
+ struct object *o = have_obj.objects[i].item;
+ o->flags |= UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
+ }
+ setup_revisions(0, NULL, &revs, NULL);
+ }
+ prepare_revision_walk(&revs);
+ mark_edges_uninteresting(revs.commits, &revs, show_edge);
+ traverse_commit_list(&revs, show_commit, show_object);
+ return 0;
+}
+
static void create_pack_file(void)
{
- /* Pipes between rev-list to pack-objects, pack-objects to us
- * and pack-objects error stream for progress bar.
- */
- int lp_pipe[2], pu_pipe[2], pe_pipe[2];
- pid_t pid_rev_list, pid_pack_objects;
+ struct async rev_list;
+ struct child_process pack_objects;
int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
char data[8193], progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;
+ const char *argv[10];
+ int arg = 0;
- if (pipe(lp_pipe) < 0)
- die("git-upload-pack: unable to create pipe");
- pid_rev_list = fork();
- if (pid_rev_list < 0)
+ rev_list.proc = do_rev_list;
+ /* .data is just a boolean: any non-NULL value will do */
+ rev_list.data = create_full_pack ? &rev_list : NULL;
+ if (start_async(&rev_list))
die("git-upload-pack: unable to fork git-rev-list");
- if (!pid_rev_list) {
- int i;
- struct rev_info revs;
-
- close(lp_pipe[0]);
- pack_pipe = fdopen(lp_pipe[1], "w");
-
- if (create_full_pack)
- use_thin_pack = 0; /* no point doing it */
- init_revisions(&revs, NULL);
- revs.tag_objects = 1;
- revs.tree_objects = 1;
- revs.blob_objects = 1;
- if (use_thin_pack)
- revs.edge_hint = 1;
-
- if (create_full_pack) {
- const char *args[] = {"rev-list", "--all", NULL};
- setup_revisions(2, args, &revs, NULL);
- } else {
- for (i = 0; i < want_obj.nr; i++) {
- struct object *o = want_obj.objects[i].item;
- /* why??? */
- o->flags &= ~UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- for (i = 0; i < have_obj.nr; i++) {
- struct object *o = have_obj.objects[i].item;
- o->flags |= UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- setup_revisions(0, NULL, &revs, NULL);
- }
- prepare_revision_walk(&revs);
- mark_edges_uninteresting(revs.commits, &revs, show_edge);
- traverse_commit_list(&revs, show_commit, show_object);
- exit(0);
- }
-
- if (pipe(pu_pipe) < 0)
- die("git-upload-pack: unable to create pipe");
- if (pipe(pe_pipe) < 0)
- die("git-upload-pack: unable to create pipe");
- pid_pack_objects = fork();
- if (pid_pack_objects < 0) {
+ argv[arg++] = "pack-objects";
+ argv[arg++] = "--stdout";
+ if (!no_progress)
+ argv[arg++] = "--progress";
+ if (use_ofs_delta)
+ argv[arg++] = "--delta-base-offset";
+ argv[arg++] = NULL;
+
+ memset(&pack_objects, 0, sizeof(pack_objects));
+ pack_objects.in = rev_list.out; /* start_command closes it */
+ pack_objects.out = -1;
+ pack_objects.err = -1;
+ pack_objects.git_cmd = 1;
+ pack_objects.argv = argv;
+
+ if (start_command(&pack_objects)) {
/* daemon sets things up to ignore TERM */
- kill(pid_rev_list, SIGKILL);
+ kill(rev_list.pid, SIGKILL);
die("git-upload-pack: unable to fork git-pack-objects");
}
- if (!pid_pack_objects) {
- const char *argv[10];
- int i = 0;
-
- dup2(lp_pipe[0], 0);
- dup2(pu_pipe[1], 1);
- dup2(pe_pipe[1], 2);
-
- close(lp_pipe[0]);
- close(lp_pipe[1]);
- close(pu_pipe[0]);
- close(pu_pipe[1]);
- close(pe_pipe[0]);
- close(pe_pipe[1]);
-
- argv[i++] = "pack-objects";
- argv[i++] = "--stdout";
- if (!no_progress)
- argv[i++] = "--progress";
- if (use_ofs_delta)
- argv[i++] = "--delta-base-offset";
- argv[i++] = NULL;
-
- execv_git_cmd(argv);
- kill(pid_rev_list, SIGKILL);
- die("git-upload-pack: unable to exec git-pack-objects");
- }
-
- close(lp_pipe[0]);
- close(lp_pipe[1]);
- /* We read from pe_pipe[0] to capture stderr output for
- * progress bar, and pu_pipe[0] to capture the pack data.
+ /* We read from pack_objects.err to capture stderr output for
+ * progress bar, and pack_objects.out to capture the pack data.
*/
- close(pe_pipe[1]);
- close(pu_pipe[1]);
while (1) {
const char *who;
@@ -214,14 +191,14 @@ static void create_pack_file(void)
pollsize = 0;
pe = pu = -1;
- if (0 <= pu_pipe[0]) {
- pfd[pollsize].fd = pu_pipe[0];
+ if (0 <= pack_objects.out) {
+ pfd[pollsize].fd = pack_objects.out;
pfd[pollsize].events = POLLIN;
pu = pollsize;
pollsize++;
}
- if (0 <= pe_pipe[0]) {
- pfd[pollsize].fd = pe_pipe[0];
+ if (0 <= pack_objects.err) {
+ pfd[pollsize].fd = pack_objects.err;
pfd[pollsize].events = POLLIN;
pe = pollsize;
pollsize++;
@@ -254,13 +231,13 @@ static void create_pack_file(void)
*cp++ = buffered;
outsz++;
}
- sz = xread(pu_pipe[0], cp,
+ sz = xread(pack_objects.out, cp,
sizeof(data) - outsz);
if (0 < sz)
;
else if (sz == 0) {
- close(pu_pipe[0]);
- pu_pipe[0] = -1;
+ close(pack_objects.out);
+ pack_objects.out = -1;
}
else
goto fail;
@@ -279,13 +256,13 @@ static void create_pack_file(void)
/* Status ready; we ship that in the side-band
* or dump to the standard error.
*/
- sz = xread(pe_pipe[0], progress,
+ sz = xread(pack_objects.err, progress,
sizeof(progress));
if (0 < sz)
send_client_data(2, progress, sz);
else if (sz == 0) {
- close(pe_pipe[0]);
- pe_pipe[0] = -1;
+ close(pack_objects.err);
+ pack_objects.err = -1;
}
else
goto fail;
@@ -293,12 +270,12 @@ static void create_pack_file(void)
}
/* See if the children are still there */
- if (pid_rev_list || pid_pack_objects) {
+ if (rev_list.pid || pack_objects.pid) {
pid = waitpid(-1, &status, WNOHANG);
if (!pid)
continue;
- who = ((pid == pid_rev_list) ? "git-rev-list" :
- (pid == pid_pack_objects) ? "git-pack-objects" :
+ who = ((pid == rev_list.pid) ? "git-rev-list" :
+ (pid == pack_objects.pid) ? "git-pack-objects" :
NULL);
if (!who) {
if (pid < 0) {
@@ -315,11 +292,11 @@ static void create_pack_file(void)
who);
goto fail;
}
- if (pid == pid_rev_list)
- pid_rev_list = 0;
- if (pid == pid_pack_objects)
- pid_pack_objects = 0;
- if (pid_rev_list || pid_pack_objects)
+ if (pid == rev_list.pid)
+ rev_list.pid = 0;
+ if (pid == pack_objects.pid)
+ pack_objects.pid = 0;
+ if (rev_list.pid || pack_objects.pid)
continue;
}
@@ -340,10 +317,10 @@ static void create_pack_file(void)
return;
}
fail:
- if (pid_pack_objects)
- kill(pid_pack_objects, SIGKILL);
- if (pid_rev_list)
- kill(pid_rev_list, SIGKILL);
+ if (pack_objects.pid)
+ kill(pack_objects.pid, SIGKILL);
+ if (rev_list.pid)
+ kill(rev_list.pid, SIGKILL);
send_client_data(3, abort_msg, sizeof(abort_msg));
die("git-upload-pack: %s", abort_msg);
}
diff --git a/wt-status.c b/wt-status.c
index 10ce6ee..03b5ec4 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -52,31 +52,34 @@ void wt_status_prepare(struct wt_status *s)
head = resolve_ref("HEAD", sha1, 0, NULL);
s->branch = head ? xstrdup(head) : NULL;
s->reference = "HEAD";
+ s->fp = stdout;
+ s->index_file = get_index_file();
}
-static void wt_status_print_cached_header(const char *reference)
+static void wt_status_print_cached_header(struct wt_status *s)
{
const char *c = color(WT_STATUS_HEADER);
- color_printf_ln(c, "# Changes to be committed:");
- if (reference) {
- color_printf_ln(c, "# (use \"git reset %s <file>...\" to unstage)", reference);
+ color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+ if (s->reference) {
+ color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
} else {
- color_printf_ln(c, "# (use \"git rm --cached <file>...\" to unstage)");
+ color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
}
- color_printf_ln(c, "#");
+ color_fprintf_ln(s->fp, c, "#");
}
-static void wt_status_print_header(const char *main, const char *sub)
+static void wt_status_print_header(struct wt_status *s,
+ const char *main, const char *sub)
{
const char *c = color(WT_STATUS_HEADER);
- color_printf_ln(c, "# %s:", main);
- color_printf_ln(c, "# (%s)", sub);
- color_printf_ln(c, "#");
+ color_fprintf_ln(s->fp, c, "# %s:", main);
+ color_fprintf_ln(s->fp, c, "# (%s)", sub);
+ color_fprintf_ln(s->fp, c, "#");
}
-static void wt_status_print_trailer(void)
+static void wt_status_print_trailer(struct wt_status *s)
{
- color_printf_ln(color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
}
static const char *quote_crlf(const char *in, char *buf, size_t sz)
@@ -108,7 +111,8 @@ static const char *quote_crlf(const char *in, char *buf, size_t sz)
return ret;
}
-static void wt_status_print_filepair(int t, struct diff_filepair *p)
+static void wt_status_print_filepair(struct wt_status *s,
+ int t, struct diff_filepair *p)
{
const char *c = color(t);
const char *one, *two;
@@ -117,36 +121,36 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p)
one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
- color_printf(color(WT_STATUS_HEADER), "#\t");
+ color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
switch (p->status) {
case DIFF_STATUS_ADDED:
- color_printf(c, "new file: %s", one);
+ color_fprintf(s->fp, c, "new file: %s", one);
break;
case DIFF_STATUS_COPIED:
- color_printf(c, "copied: %s -> %s", one, two);
+ color_fprintf(s->fp, c, "copied: %s -> %s", one, two);
break;
case DIFF_STATUS_DELETED:
- color_printf(c, "deleted: %s", one);
+ color_fprintf(s->fp, c, "deleted: %s", one);
break;
case DIFF_STATUS_MODIFIED:
- color_printf(c, "modified: %s", one);
+ color_fprintf(s->fp, c, "modified: %s", one);
break;
case DIFF_STATUS_RENAMED:
- color_printf(c, "renamed: %s -> %s", one, two);
+ color_fprintf(s->fp, c, "renamed: %s -> %s", one, two);
break;
case DIFF_STATUS_TYPE_CHANGED:
- color_printf(c, "typechange: %s", one);
+ color_fprintf(s->fp, c, "typechange: %s", one);
break;
case DIFF_STATUS_UNKNOWN:
- color_printf(c, "unknown: %s", one);
+ color_fprintf(s->fp, c, "unknown: %s", one);
break;
case DIFF_STATUS_UNMERGED:
- color_printf(c, "unmerged: %s", one);
+ color_fprintf(s->fp, c, "unmerged: %s", one);
break;
default:
die("bug: unhandled diff status %c", p->status);
}
- printf("\n");
+ fprintf(s->fp, "\n");
}
static void wt_status_print_updated_cb(struct diff_queue_struct *q,
@@ -160,14 +164,14 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
if (q->queue[i]->status == 'U')
continue;
if (!shown_header) {
- wt_status_print_cached_header(s->reference);
+ wt_status_print_cached_header(s);
s->commitable = 1;
shown_header = 1;
}
- wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
+ wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
}
if (shown_header)
- wt_status_print_trailer();
+ wt_status_print_trailer(s);
}
static void wt_status_print_changed_cb(struct diff_queue_struct *q,
@@ -184,18 +188,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
msg = use_add_rm_msg;
break;
}
- wt_status_print_header("Changed but not updated", msg);
+ wt_status_print_header(s, "Changed but not updated", msg);
}
for (i = 0; i < q->nr; i++)
- wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
+ wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
if (q->nr)
- wt_status_print_trailer();
+ wt_status_print_trailer(s);
}
static void wt_read_cache(struct wt_status *s)
{
discard_cache();
- read_cache();
+ read_cache_from(s->index_file);
}
static void wt_status_print_initial(struct wt_status *s)
@@ -206,16 +210,16 @@ static void wt_status_print_initial(struct wt_status *s)
wt_read_cache(s);
if (active_nr) {
s->commitable = 1;
- wt_status_print_cached_header(NULL);
+ wt_status_print_cached_header(s);
}
for (i = 0; i < active_nr; i++) {
- color_printf(color(WT_STATUS_HEADER), "#\t");
- color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
+ color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
+ color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
quote_crlf(active_cache[i]->name,
buf, sizeof(buf)));
}
if (active_nr)
- wt_status_print_trailer();
+ wt_status_print_trailer(s);
}
static void wt_status_print_updated(struct wt_status *s)
@@ -282,12 +286,12 @@ static void wt_status_print_untracked(struct wt_status *s)
}
if (!shown_header) {
s->workdir_untracked = 1;
- wt_status_print_header("Untracked files",
+ wt_status_print_header(s, "Untracked files",
use_add_to_include_msg);
shown_header = 1;
}
- color_printf(color(WT_STATUS_HEADER), "#\t");
- color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
+ color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
+ color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s",
ent->len, ent->name);
}
}
@@ -317,14 +321,14 @@ void wt_status_print(struct wt_status *s)
branch_name = "";
on_what = "Not currently on any branch.";
}
- color_printf_ln(color(WT_STATUS_HEADER),
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
"# %s%s", on_what, branch_name);
}
if (s->is_initial) {
- color_printf_ln(color(WT_STATUS_HEADER), "#");
- color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
- color_printf_ln(color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
wt_status_print_initial(s);
}
else {
@@ -338,7 +342,7 @@ void wt_status_print(struct wt_status *s)
wt_status_print_verbose(s);
if (!s->commitable) {
if (s->amend)
- printf("# No changes\n");
+ fprintf(s->fp, "# No changes\n");
else if (s->workdir_dirty)
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
else if (s->workdir_untracked)
diff --git a/wt-status.h b/wt-status.h
index cfea4ae..7744932 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -1,6 +1,8 @@
#ifndef STATUS_H
#define STATUS_H
+#include <stdio.h>
+
enum color_wt_status {
WT_STATUS_HEADER,
WT_STATUS_UPDATED,
@@ -19,6 +21,8 @@ struct wt_status {
int commitable;
int workdir_dirty;
int workdir_untracked;
+ const char *index_file;
+ FILE *fp;
};
int git_status_config(const char *var, const char *value);