summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.4.4.txt32
-rw-r--r--Documentation/RelNotes-1.5.5.txt4
-rw-r--r--Documentation/config.txt44
-rw-r--r--Documentation/git-am.txt6
-rw-r--r--Documentation/git-fetch-pack.txt8
-rw-r--r--Documentation/git-mergetool.txt38
-rw-r--r--Documentation/git-pack-objects.txt5
-rw-r--r--Documentation/git-reflog.txt14
-rw-r--r--Documentation/git-stash.txt13
-rw-r--r--Documentation/git-submodule.txt5
-rw-r--r--Documentation/git-whatchanged.txt9
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/pretty-options.txt3
-rw-r--r--Documentation/technical/api-run-command.txt7
-rw-r--r--Makefile12
-rw-r--r--builtin-describe.c17
-rw-r--r--builtin-fetch-pack.c9
-rw-r--r--builtin-fetch.c52
-rw-r--r--builtin-log.c5
-rw-r--r--builtin-name-rev.c59
-rw-r--r--builtin-pack-objects.c24
-rw-r--r--builtin-reflog.c102
-rw-r--r--builtin-reset.c47
-rw-r--r--builtin-shortlog.c5
-rw-r--r--compat/snprintf.c40
-rw-r--r--config.mak.in1
-rw-r--r--configure.ac34
-rwxr-xr-xcontrib/completion/git-completion.bash10
-rw-r--r--contrib/emacs/git.el2
-rw-r--r--contrib/hooks/post-receive-email2
-rw-r--r--fast-import.c3
-rw-r--r--fetch-pack.h3
-rwxr-xr-xgit-am.sh44
-rw-r--r--git-compat-util.h15
-rwxr-xr-xgit-cvsserver.perl2
-rwxr-xr-xgit-filter-branch.sh11
-rw-r--r--git-gui/Makefile5
-rwxr-xr-xgit-merge.sh2
-rwxr-xr-xgit-mergetool.sh161
-rwxr-xr-xgit-rebase.sh2
-rwxr-xr-xgit-send-email.perl3
-rwxr-xr-xgit-stash.sh36
-rwxr-xr-xgit-submodule.sh50
-rwxr-xr-xgit-svn.perl7
-rwxr-xr-xgitweb/gitweb.perl85
-rw-r--r--ident.c2
-rw-r--r--path.c2
-rw-r--r--quote.c2
-rw-r--r--refs.c4
-rw-r--r--refs.h6
-rw-r--r--run-command.c14
-rwxr-xr-xt/t1410-reflog.sh27
-rwxr-xr-xt/t3903-stash.sh50
-rwxr-xr-xt/t4150-am-subdir.sh72
-rwxr-xr-xt/t5305-include-tag.sh84
-rwxr-xr-xt/t5503-tagfollow.sh150
-rwxr-xr-xt/t7003-filter-branch.sh24
-rw-r--r--t/t7610-mergetool.sh46
-rwxr-xr-xt/t9300-fast-import.sh2
-rw-r--r--t/test-lib.sh24
-rw-r--r--transport.c5
-rw-r--r--transport.h3
-rw-r--r--upload-pack.c19
63 files changed, 1273 insertions, 304 deletions
diff --git a/Documentation/RelNotes-1.5.4.4.txt b/Documentation/RelNotes-1.5.4.4.txt
index 5635977..89fa6d0 100644
--- a/Documentation/RelNotes-1.5.4.4.txt
+++ b/Documentation/RelNotes-1.5.4.4.txt
@@ -37,10 +37,30 @@ Fixes since v1.5.4.3
* "git revert" did not properly fail when attempting to run with a
dirty index.
-Also included are a handful documentation updates.
+ * "git merge --no-commit --no-ff <other>" incorrectly made commits.
+
+ * "git merge --squash --no-ff <other>", which is a nonsense combination
+ of options, was not rejected.
+
+ * "git ls-remote" and "git remote show" against an empty repository
+ failed, instead of just giving an empty result (regression).
+
+ * "git fast-import" did not handle a renamed path whose name needs to be
+ quoted, due to a bug in unquote_c_style() function.
+
+ * "git cvsexportcommit" was confused when multiple files with the same
+ basename needed to be pushed out in the same commit.
+
+ * "git daemon" did not send early errors to syslog.
----
-exec >/var/tmp/1
-echo O=$(git describe maint)
-O=v1.5.4.3-32-g0f2d447
-git shortlog --no-merges $O..maint
+ * "git log --merge" did not work well with --left-right option.
+
+ * "git svn" promprted for client cert password every time it accessed the
+ server.
+
+ * The reset command in "git fast-import" data stream was documented to
+ end with an optional LF, but it actually required one.
+
+ * "git svn dcommit/rebase" did not honor --rewrite-root option.
+
+Also included are a handful documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
index b57fa1e..874dad9 100644
--- a/Documentation/RelNotes-1.5.5.txt
+++ b/Documentation/RelNotes-1.5.5.txt
@@ -160,13 +160,9 @@ Fixes since v1.5.4
All of the fixes in v1.5.4 maintenance series are included in
this release, unless otherwise noted.
- * "git-daemon" did not send early errors to syslog.
-
* "git-http-push" did not allow deletion of remote ref with the usual
"push <remote> :<branch>" syntax.
- * "git-log --merge" did not well work with --left-right option.
-
* "git-rebase --abort" did not go back to the right location if
"git-reset" was run during the "git-rebase" session.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 4027726..c5e094a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -556,6 +556,11 @@ format.suffix::
`.patch`. Use this variable to change that suffix (make sure to
include the dot if you want it).
+format.pretty::
+ The default pretty format for log/show/whatchanged command,
+ See linkgit:git-log[1], linkgit:git-show[1],
+ linkgit:git-whatchanged[1].
+
gc.aggressiveWindow::
The window size parameter used in the delta compression
algorithm used by 'git gc --aggressive'. This defaults
@@ -749,8 +754,10 @@ merge.summary::
merge.tool::
Controls which merge resolution program is used by
- linkgit:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff",
- "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
+ linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
+ "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+ "opendiff". Any other value is treated is custom merge tool
+ and there must be a corresponing mergetool.<tool>.cmd option.
merge.verbosity::
Controls the amount of output shown by the recursive merge
@@ -777,6 +784,31 @@ mergetool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
+mergetool.<tool>.cmd::
+ Specify the command to invoke the specified merge tool. The
+ specified command is evaluated in shell with the following
+ variables available: 'BASE' is the name of a temporary file
+ containing the common base of the files to be merged, if available;
+ 'LOCAL' is the name of a temporary file containing the contents of
+ the file on the current branch; 'REMOTE' is the name of a temporary
+ file containing the contents of the file from the branch being
+ merged; 'MERGED' contains the name of the file to which the merge
+ tool should write the results of a successful merge.
+
+mergetool.<tool>.trustExitCode::
+ For a custom merge command, specify whether the exit code of
+ the merge command can be used to determine whether the merge was
+ successful. If this is not set to true then the merge target file
+ timestamp is checked and the merge assumed to have been successful
+ if the file has been updated, otherwise the user is prompted to
+ indicate the success of the merge.
+
+mergetool.keepBackup::
+ After performing a merge, the original file with conflict markers
+ can be saved as a file with a `.orig` extension. If this variable
+ is set to `false` then this file is not preserved. Defaults to
+ `true` (i.e. keep the backup files).
+
pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.
@@ -864,15 +896,15 @@ remote.<name>.skipDefaultUpdate::
remote.<name>.receivepack::
The default program to execute on the remote side when pushing. See
- option \--exec of linkgit:git-push[1].
+ option \--receive-pack of linkgit:git-push[1].
remote.<name>.uploadpack::
The default program to execute on the remote side when fetching. See
- option \--exec of linkgit:git-fetch-pack[1].
+ option \--upload-pack of linkgit:git-fetch-pack[1].
remote.<name>.tagopt::
- Setting this value to --no-tags disables automatic tag following when fetching
- from remote <name>
+ Setting this value to \--no-tags disables automatic tag following when
+ fetching from remote <name>
remotes.<group>::
The list of remotes which are fetched by "git remote update
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e640fc7..2387a8d 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
SYNOPSIS
--------
[verse]
-'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
+'git-am' [--signoff] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--binary]
[--whitespace=<option>] [-C<n>] [-p<n>]
<mbox>|<Maildir>...
@@ -32,10 +32,6 @@ OPTIONS
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
--d=<dir>, --dotest=<dir>::
- Instead of `.dotest` directory, use <dir> as a working
- area to store extracted patches.
-
-k, --keep::
Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 2b8ffe5..57598eb 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository
SYNOPSIS
--------
-'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
+'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
DESCRIPTION
-----------
@@ -45,6 +45,12 @@ OPTIONS
Spend extra cycles to minimize the number of objects to be sent.
Use it on slower connection.
+\--include-tag::
+ If the remote side supports it, annotated tags objects will
+ be downloaded on the same connection as the other objects if
+ the object the tag references is downloaded. The caller must
+ otherwise determine the tags this option made available.
+
\--upload-pack=<git-upload-pack>::
Use this to specify the path to 'git-upload-pack' on the
remote side, if is not found on your $PATH.
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 50f106e..8ed4494 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -12,12 +12,12 @@ SYNOPSIS
DESCRIPTION
-----------
-Use 'git mergetool' to run one of several merge utilities to resolve
+Use `git mergetool` to run one of several merge utilities to resolve
merge conflicts. It is typically run after linkgit:git-merge[1].
If one or more <file> parameters are given, the merge tool program will
be run to resolve differences on each file. If no <file> names are
-specified, 'git mergetool' will run the merge tool program on every file
+specified, `git mergetool` will run the merge tool program on every file
with merge conflicts.
OPTIONS
@@ -27,16 +27,38 @@ OPTIONS
Valid merge tools are:
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'
+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
+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.
+`mergetool.kdiff3.path`. Otherwise, `git mergetool` assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs
+`git mergetool` can be customized to run an alternative program
+by specifying the command line to invoke in a configration
+variable `mergetool.<tool>.cmd`.
++
+When `git mergetool` is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration
+variable) the configured command line will be invoked with `$BASE`
+set to the name of a temporary file containing the common base for
+the merge, if available; `$LOCAL` set to the name of a temporary
+file containing the contents of the file on the current branch;
+`$REMOTE` set to the name of a temporary file containing the
+contents of the file to be merged, and `$MERGED` set to the name
+of the file to which the merge tool should write the result of the
+merge resolution.
++
+If the custom merge tool correctly indicates the success of a
+merge resolution with its exit code then the configuration
+variable `mergetool.<tool>.trustExitCode` can be set to `true`.
+Otherwise, `git mergetool` will prompt the user to indicate the
+success of the resolution after the custom tool has exited.
Author
------
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 5c1bd3b..eed0a94 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -73,6 +73,11 @@ base-name::
as if all refs under `$GIT_DIR/refs` are specified to be
included.
+--include-tag::
+ Include unasked-for annotated tags if the object they
+ reference was included in the resulting packfile. This
+ can be useful to send new tags to native git clients.
+
--window=[N], --depth=[N]::
These two options affect how the objects contained in
the pack are stored using delta compression. The
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index f9bba36..047e3ce 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -19,6 +19,8 @@ depending on the subcommand:
git reflog expire [--dry-run] [--stale-fix] [--verbose]
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
+git reflog delete ref@\{specifier\}...
+
git reflog [show] [log-options] [<ref>]
Reflog is a mechanism to record when the tip of branches are
@@ -43,6 +45,9 @@ two moves ago", `master@\{one.week.ago\}` means "where master used to
point to one week ago", and so on. See linkgit:git-rev-parse[1] for
more details.
+To delete single entries from the reflog, use the subcommand "delete"
+and specify the _exact_ entry (e.g. ``git reflog delete master@\{2\}'').
+
OPTIONS
-------
@@ -75,6 +80,15 @@ them.
--all::
Instead of listing <refs> explicitly, prune all refs.
+--updateref::
+ Update the ref with the sha1 of the top reflog entry (i.e.
+ <ref>@\{0\}) after expiring or deleting.
+
+--rewrite::
+ While expiring or deleting, adjust each reflog entry to ensure
+ that the `old` sha1 field points to the `new` sha1 field of the
+ previous entry.
+
--verbose::
Print extra information on screen.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 48e6f5a..8dc35d4 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -8,7 +8,7 @@ git-stash - Stash the changes in a dirty working directory away
SYNOPSIS
--------
[verse]
-'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
'git-stash' [save [<message>]]
DESCRIPTION
@@ -85,6 +85,17 @@ clear::
Remove all the stashed states. Note that those states will then
be subject to pruning, and may be difficult or impossible to recover.
+drop [<stash>]::
+
+ Remove a single stashed state from the stash list. When no `<stash>`
+ is given, it removes the latest one. i.e. `stash@\{0}`
+
+pop [<stash>]::
+
+ Remove a single stashed state from the stash list and apply on top
+ of the current working tree state. When no `<stash>` is given,
+ `stash@\{0}` is assumed. See also `apply`.
+
DISCUSSION
----------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index e818e6e..b4d0160 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -18,8 +18,9 @@ COMMANDS
--------
add::
Add the given repository as a submodule at the given path
- to the changeset to be committed next. In particular, the
- repository is cloned at the specified path, added to the
+ to the changeset to be committed next. If path is a valid
+ repository within the project, it is added as is. Otherwise,
+ repository is cloned at the specified path. path is added to the
changeset and registered in .gitmodules. If no path is
specified, the path is deduced from the repository specification.
If the repository url begins with ./ or ../, it is stored as
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 54947b6..a6e7bd4 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -38,11 +38,6 @@ OPTIONS
Show git internal diff output, but for the whole tree,
not just the top level.
---pretty=<format>::
- Controls the output format for the commit logs.
- <format> can be one of 'raw', 'medium', 'short', 'full',
- and 'oneline'.
-
-m::
By default, differences for merge commits are not shown.
With this flag, show differences to that commit from all
@@ -51,6 +46,10 @@ OPTIONS
However, it is not very useful in general, although it
*is* useful on a file-by-file basis.
+include::pretty-options.txt[]
+
+include::pretty-formats.txt[]
+
Examples
--------
git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 741ae0e..3ed24d4 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
+* link:v1.5.4.4/git.html[documentation for release 1.5.4.4]
* release notes for
+ link:RelNotes-1.5.4.4.txt[1.5.4.4],
link:RelNotes-1.5.4.3.txt[1.5.4.3],
link:RelNotes-1.5.4.2.txt[1.5.4.2],
link:RelNotes-1.5.4.1.txt[1.5.4.1],
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 973d8dd..6d66c74 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -4,6 +4,9 @@
where '<format>' can be one of 'oneline', 'short', 'medium',
'full', 'fuller', 'email', 'raw' and 'format:<string>'.
When omitted, the format defaults to 'medium'.
++
+Note: you can specify the default pretty format in the repository
+configuration (see linkgit:git-config[1]).
--abbrev-commit::
Instead of showing the full 40-byte hexadecimal commit object
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index fde3b45..c364a22 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -111,9 +111,10 @@ stderr as follows:
.no_stdin, .no_stdout, .no_stderr: The respective channel is
redirected to /dev/null.
- .stdout_to_stderr: stdout of the child is redirected to the
- parent's stderr (i.e. *not* to what .err or
- .no_stderr specify).
+ .stdout_to_stderr: stdout of the child is redirected to its
+ stderr. This happens after stderr is itself redirected.
+ So stdout will follow stderr to wherever it is
+ redirected.
To modify the environment of the sub-process, specify an array of
string pointers (NULL terminated) in .env:
diff --git a/Makefile b/Makefile
index ca5aad9..7fbb815 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,10 @@ all::
# Define V=1 to have a more verbose compile.
#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
# when attempting to read from an fopen'ed directory.
#
@@ -478,6 +482,7 @@ ifeq ($(uname_S),FreeBSD)
NO_MEMMEM = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
+ DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
@@ -629,6 +634,10 @@ endif
ifdef NO_C99_FORMAT
BASIC_CFLAGS += -DNO_C99_FORMAT
endif
+ifdef SNPRINTF_RETURNS_BOGUS
+ COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+ COMPAT_OBJS += compat/snprintf.o
+endif
ifdef FREAD_READS_DIRECTORIES
COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
COMPAT_OBJS += compat/fopen.o
@@ -747,6 +756,9 @@ ifdef THREADED_DELTA_SEARCH
EXTLIBS += -lpthread
LIB_OBJS += thread-utils.o
endif
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+ COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks
diff --git a/builtin-describe.c b/builtin-describe.c
index 7a5ab01..df554b3 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -21,6 +21,7 @@ static int longformat;
static int abbrev = DEFAULT_ABBREV;
static int max_candidates = 10;
const char *pattern = NULL;
+static int always;
struct commit_name {
struct tag *tag;
@@ -257,8 +258,14 @@ static void describe(const char *arg, int last_one)
}
}
- if (!match_cnt)
- die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+ if (!match_cnt) {
+ const unsigned char *sha1 = cmit->object.sha1;
+ if (always) {
+ printf("%s\n", find_unique_abbrev(sha1, abbrev));
+ return;
+ }
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+ }
qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -311,6 +318,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
"consider <n> most recent tags (default: 10)"),
OPT_STRING(0, "match", &pattern, "pattern",
"only consider tags matching <pattern>"),
+ OPT_BOOLEAN(0, "always", &always,
+ "show abbreviated commit object as fallback"),
OPT_END(),
};
@@ -326,11 +335,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die("--long is incompatible with --abbrev=0");
if (contains) {
- const char **args = xmalloc((6 + argc) * sizeof(char*));
+ const char **args = xmalloc((7 + argc) * sizeof(char*));
int i = 0;
args[i++] = "name-rev";
args[i++] = "--name-only";
args[i++] = "--no-undefined";
+ if (always)
+ args[i++] = "--always";
if (!all) {
args[i++] = "--tags";
if (pattern) {
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 29b38e4..7b28024 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -18,7 +18,7 @@ static struct fetch_pack_args args = {
};
static const char fetch_pack_usage[] =
-"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
#define COMPLETE (1U << 0)
#define COMMON (1U << 1)
@@ -176,13 +176,14 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
if (!fetching)
- packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
+ packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
sha1_to_hex(remote),
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""),
(args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""),
+ (args.include_tag ? " include-tag" : ""),
" ofs-delta");
else
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -683,6 +684,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.use_thin_pack = 1;
continue;
}
+ if (!strcmp("--include-tag", arg)) {
+ args.include_tag = 1;
+ continue;
+ }
if (!strcmp("--all", arg)) {
args.fetch_all = 1;
continue;
diff --git a/builtin-fetch.c b/builtin-fetch.c
index ac335f2..55f611e 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -101,6 +101,10 @@ static void add_merge_config(struct ref **head,
}
}
+static void find_non_local_tags(struct transport *transport,
+ struct ref **head,
+ struct ref ***tail);
+
static struct ref *get_ref_map(struct transport *transport,
struct refspec *refs, int ref_count, int tags,
int *autotags)
@@ -157,8 +161,11 @@ static struct ref *get_ref_map(struct transport *transport,
if (!ref_map)
die("Couldn't find remote ref HEAD");
ref_map->merge = 1;
+ tail = &ref_map->next;
}
}
+ if (tags == TAGS_DEFAULT && *autotags)
+ find_non_local_tags(transport, &ref_map, &tail);
ref_remove_duplicates(ref_map);
return ref_map;
@@ -452,18 +459,28 @@ static int add_existing(const char *refname, const unsigned char *sha1,
return 0;
}
-static struct ref *find_non_local_tags(struct transport *transport,
- struct ref *fetch_map)
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+ struct ref *rm = *head;
+ while (rm) {
+ if (!hashcmp(rm->old_sha1, sha1))
+ return 1;
+ rm = rm->next;
+ }
+ return 0;
+}
+
+static void find_non_local_tags(struct transport *transport,
+ struct ref **head,
+ struct ref ***tail)
{
- static struct path_list existing_refs = { NULL, 0, 0, 0 };
+ struct path_list existing_refs = { NULL, 0, 0, 0 };
struct path_list new_refs = { NULL, 0, 0, 1 };
char *ref_name;
int ref_name_len;
const unsigned char *ref_sha1;
const struct ref *tag_ref;
struct ref *rm = NULL;
- struct ref *ref_map = NULL;
- struct ref **tail = &ref_map;
const struct ref *ref;
for_each_ref(add_existing, &existing_refs);
@@ -489,7 +506,8 @@ static struct ref *find_non_local_tags(struct transport *transport,
if (!path_list_has_path(&existing_refs, ref_name) &&
!path_list_has_path(&new_refs, ref_name) &&
- has_sha1_file(ref->old_sha1)) {
+ (has_sha1_file(ref->old_sha1) ||
+ will_fetch(head, ref->old_sha1))) {
path_list_insert(ref_name, &new_refs);
rm = alloc_ref(strlen(ref_name) + 1);
@@ -498,19 +516,19 @@ static struct ref *find_non_local_tags(struct transport *transport,
strcpy(rm->peer_ref->name, ref_name);
hashcpy(rm->old_sha1, ref_sha1);
- *tail = rm;
- tail = &rm->next;
+ **tail = rm;
+ *tail = &rm->next;
}
free(ref_name);
}
-
- return ref_map;
+ path_list_clear(&existing_refs, 0);
+ path_list_clear(&new_refs, 0);
}
static int do_fetch(struct transport *transport,
struct refspec *refs, int ref_count)
{
- struct ref *ref_map, *fetch_map;
+ struct ref *ref_map;
struct ref *rm;
int autotags = (transport->remote->fetch_tags == 1);
if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
@@ -537,26 +555,28 @@ static int do_fetch(struct transport *transport,
read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
}
+ if (tags == TAGS_DEFAULT && autotags)
+ transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
if (fetch_refs(transport, ref_map)) {
free_refs(ref_map);
return 1;
}
-
- fetch_map = ref_map;
+ free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
* following ... */
if (tags == TAGS_DEFAULT && autotags) {
- ref_map = find_non_local_tags(transport, fetch_map);
+ struct ref **tail = &ref_map;
+ ref_map = NULL;
+ find_non_local_tags(transport, &ref_map, &tail);
if (ref_map) {
+ transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
fetch_refs(transport, ref_map);
}
free_refs(ref_map);
}
- free_refs(fetch_map);
-
transport_disconnect(transport);
return 0;
diff --git a/builtin-log.c b/builtin-log.c
index fe8fc6f..d983cbc 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -20,6 +20,7 @@
static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
{
@@ -54,6 +55,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
+ if (fmt_pretty)
+ rev->commit_format = get_commit_format(fmt_pretty);
rev->verbose_header = 1;
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
rev->show_root_diff = default_show_root;
@@ -221,6 +224,8 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value)
{
+ if (!strcmp(var, "format.pretty"))
+ return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix")) {
if (!value)
config_error_nonbool(var);
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index f22c8b5..384da4d 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -125,7 +125,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
}
/* returns a static buffer */
-static const char *get_rev_name(struct object *o)
+static const char *get_rev_name(const struct object *o)
{
static char buffer[1024];
struct rev_name *n;
@@ -151,6 +151,26 @@ static const char *get_rev_name(struct object *o)
}
}
+static void show_name(const struct object *obj,
+ const char *caller_name,
+ int always, int allow_undefined, int name_only)
+{
+ const char *name;
+ const unsigned char *sha1 = obj->sha1;
+
+ if (!name_only)
+ printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+ name = get_rev_name(obj);
+ if (name)
+ printf("%s\n", name);
+ else if (allow_undefined)
+ printf("undefined\n");
+ else if (always)
+ printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ else
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+}
+
static char const * const name_rev_usage[] = {
"git-name-rev [options] ( --all | --stdin | <commit>... )",
NULL
@@ -159,7 +179,7 @@ static char const * const name_rev_usage[] = {
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
- int all = 0, transform_stdin = 0, allow_undefined = 1;
+ int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
struct name_ref_data data = { 0, 0, NULL };
struct option opts[] = {
OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -170,6 +190,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+ OPT_BOOLEAN(0, "always", &always,
+ "show abbreviated commit object as fallback"),
OPT_END(),
};
@@ -258,35 +280,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
int i, max;
max = get_max_object_index();
- for (i = 0; i < max; i++) {
- struct object * obj = get_indexed_object(i);
- const char *name;
- if (!obj)
- continue;
- if (!data.name_only)
- printf("%s ", sha1_to_hex(obj->sha1));
- name = get_rev_name(obj);
- if (name)
- printf("%s\n", name);
- else if (allow_undefined)
- printf("undefined\n");
- else
- die("cannot describe '%s'", sha1_to_hex(obj->sha1));
- }
+ for (i = 0; i < max; i++)
+ show_name(get_indexed_object(i), NULL,
+ always, allow_undefined, data.name_only);
} else {
int i;
- for (i = 0; i < revs.nr; i++) {
- const char *name;
- if (!data.name_only)
- printf("%s ", revs.objects[i].name);
- name = get_rev_name(revs.objects[i].item);
- if (name)
- printf("%s\n", name);
- else if (allow_undefined)
- printf("undefined\n");
- else
- die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
- }
+ for (i = 0; i < revs.nr; i++)
+ show_name(revs.objects[i].item, revs.objects[i].name,
+ always, allow_undefined, data.name_only);
}
return 0;
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 2799e68..f504cff 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -15,6 +15,7 @@
#include "revision.h"
#include "list-objects.h"
#include "progress.h"
+#include "refs.h"
#ifdef THREADED_DELTA_SEARCH
#include "thread-utils.h"
@@ -27,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
[--window=N] [--window-memory=N] [--depth=N] \n\
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
- [--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
+ [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+ [<ref-list | <object-list]";
struct object_entry {
struct pack_idx_entry idx;
@@ -63,7 +65,7 @@ static struct pack_idx_entry **written_list;
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable;
+static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
static int local;
static int incremental;
static int allow_ofs_delta;
@@ -1630,6 +1632,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
#endif
+static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+ unsigned char peeled[20];
+
+ if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+ !peel_ref(path, peeled) && /* peelable? */
+ !is_null_sha1(peeled) && /* annotated tag? */
+ locate_object_entry(peeled)) /* object packed? */
+ add_object_entry(sha1, OBJ_TAG, NULL, 0);
+ return 0;
+}
+
static void prepare_pack(int window, int depth)
{
struct object_entry **delta_list;
@@ -2033,6 +2047,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
keep_unreachable = 1;
continue;
}
+ if (!strcmp("--include-tag", arg)) {
+ include_tag = 1;
+ continue;
+ }
if (!strcmp("--unpacked", arg) ||
!prefixcmp(arg, "--unpacked=") ||
!strcmp("--reflog", arg) ||
@@ -2109,6 +2127,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
+ if (include_tag && nr_result)
+ for_each_ref(add_ref_tag, NULL);
stop_progress(&progress_state);
if (non_empty && !nr_result)
diff --git a/builtin-reflog.c b/builtin-reflog.c
index ab53c8c..280e24e 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -14,6 +14,8 @@
static const char reflog_expire_usage[] =
"git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+static const char reflog_delete_usage[] =
+"git-reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
static unsigned long default_reflog_expire;
static unsigned long default_reflog_expire_unreachable;
@@ -22,9 +24,12 @@ struct cmd_reflog_expire_cb {
struct rev_info revs;
int dry_run;
int stalefix;
+ int rewrite;
+ int updateref;
int verbose;
unsigned long expire_total;
unsigned long expire_unreachable;
+ int recno;
};
struct expire_reflog_cb {
@@ -32,6 +37,7 @@ struct expire_reflog_cb {
const char *ref;
struct commit *ref_commit;
struct cmd_reflog_expire_cb *cmd;
+ unsigned char last_kept_sha1[20];
};
struct collected_reflog {
@@ -213,6 +219,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if (timestamp < cb->cmd->expire_total)
goto prune;
+ if (cb->cmd->rewrite)
+ osha1 = cb->last_kept_sha1;
+
old = new = NULL;
if (cb->cmd->stalefix &&
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
@@ -230,6 +239,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
}
+ if (cb->cmd->recno && --(cb->cmd->recno) == 0)
+ goto prune;
+
if (cb->newlog) {
char sign = (tz < 0) ? '-' : '+';
int zone = (tz < 0) ? (-tz) : tz;
@@ -237,6 +249,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
sha1_to_hex(osha1), sha1_to_hex(nsha1),
email, timestamp, sign, zone,
message);
+ hashcpy(cb->last_kept_sha1, nsha1);
}
if (cb->cmd->verbose)
printf("keep %s", message);
@@ -280,10 +293,20 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
status |= error("%s: %s", strerror(errno),
newlog_path);
unlink(newlog_path);
+ } else if (cmd->updateref &&
+ (write_in_full(lock->lock_fd,
+ sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+ write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+ close_ref(lock) < 0)) {
+ status |= error("Couldn't write %s",
+ lock->lk->filename);
+ unlink(newlog_path);
} else if (rename(newlog_path, log_file)) {
status |= error("cannot rename %s to %s",
newlog_path, log_file);
unlink(newlog_path);
+ } else if (cmd->updateref && commit_ref(lock)) {
+ status |= error("Couldn't set %s", lock->ref_name);
}
}
free(newlog_path);
@@ -358,6 +381,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
cb.expire_unreachable = approxidate(arg + 21);
else if (!strcmp(arg, "--stale-fix"))
cb.stalefix = 1;
+ else if (!strcmp(arg, "--rewrite"))
+ cb.rewrite = 1;
+ else if (!strcmp(arg, "--updateref"))
+ cb.updateref = 1;
else if (!strcmp(arg, "--all"))
do_all = 1;
else if (!strcmp(arg, "--verbose"))
@@ -406,6 +433,78 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
return status;
}
+static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+ const char *email, unsigned long timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct cmd_reflog_expire_cb *cb = cb_data;
+ if (!cb->expire_total || timestamp < cb->expire_total)
+ cb->recno++;
+ return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+ struct cmd_reflog_expire_cb cb;
+ int i, status = 0;
+
+ memset(&cb, 0, sizeof(cb));
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+ cb.dry_run = 1;
+ else if (!strcmp(arg, "--rewrite"))
+ cb.rewrite = 1;
+ else if (!strcmp(arg, "--updateref"))
+ cb.updateref = 1;
+ else if (!strcmp(arg, "--verbose"))
+ cb.verbose = 1;
+ else if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ else if (arg[0] == '-')
+ usage(reflog_delete_usage);
+ else
+ break;
+ }
+
+ if (argc - i < 1)
+ return error("Nothing to delete?");
+
+ for ( ; i < argc; i++) {
+ const char *spec = strstr(argv[i], "@{");
+ unsigned char sha1[20];
+ char *ep, *ref;
+ int recno;
+
+ if (!spec) {
+ status |= error("Not a reflog: %s", argv[i]);
+ continue;
+ }
+
+ if (!dwim_ref(argv[i], spec - argv[i], sha1, &ref)) {
+ status |= error("%s points nowhere!", argv[i]);
+ continue;
+ }
+
+ recno = strtoul(spec + 2, &ep, 10);
+ if (*ep == '}') {
+ cb.recno = -recno;
+ for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ } else {
+ cb.expire_total = approxidate(spec + 2);
+ for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ cb.expire_total = 0;
+ }
+
+ status |= expire_reflog(ref, sha1, 0, &cb);
+ free(ref);
+ }
+ return status;
+}
+
/*
* main "reflog"
*/
@@ -425,6 +524,9 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "expire"))
return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+ if (!strcmp(argv[1], "delete"))
+ return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
/* Not a recognized reflog command..*/
usage(reflog_usage);
}
diff --git a/builtin-reset.c b/builtin-reset.c
index bb3e192..79424bb 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -17,9 +17,13 @@
#include "diffcore.h"
#include "tree.h"
#include "branch.h"
+#include "parse-options.h"
-static const char builtin_reset_usage[] =
-"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
+static const char * const git_reset_usage[] = {
+ "git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
+ "git-reset [--mixed] <commit> [--] <paths>...",
+ NULL
+};
static char *args_to_str(const char **argv)
{
@@ -165,40 +169,31 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
int cmd_reset(int argc, const char **argv, const char *prefix)
{
- int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
+ int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
struct commit *commit;
char *reflog_action, msg[1024];
+ const struct option options[] = {
+ OPT_SET_INT(0, "mixed", &reset_type,
+ "reset HEAD and index", MIXED),
+ OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+ OPT_SET_INT(0, "hard", &reset_type,
+ "reset HEAD, index and working tree", HARD),
+ OPT_BOOLEAN('q', NULL, &quiet,
+ "disable showing new HEAD in hard reset"),
+ OPT_END()
+ };
git_config(git_default_config);
+ argc = parse_options(argc, argv, options, git_reset_usage,
+ PARSE_OPT_KEEP_DASHDASH);
reflog_action = args_to_str(argv);
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
- while (i < argc) {
- if (!strcmp(argv[i], "--mixed")) {
- reset_type = MIXED;
- i++;
- }
- else if (!strcmp(argv[i], "--soft")) {
- reset_type = SOFT;
- i++;
- }
- else if (!strcmp(argv[i], "--hard")) {
- reset_type = HARD;
- i++;
- }
- else if (!strcmp(argv[i], "-q")) {
- quiet = 1;
- i++;
- }
- else
- break;
- }
-
- if (i < argc && argv[i][0] != '-')
+ if (i < argc && strcmp(argv[i], "--"))
rev = argv[i++];
if (get_sha1(rev, sha1))
@@ -211,8 +206,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (i < argc && !strcmp(argv[i], "--"))
i++;
- else if (i < argc && argv[i][0] == '-')
- usage(builtin_reset_usage);
/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index af31aba..b22b0ed 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -70,11 +70,12 @@ static void insert_one_record(struct shortlog *log,
else
free(buffer);
+ /* Skip any leading whitespace, including any blank lines. */
+ while (*oneline && isspace(*oneline))
+ oneline++;
eol = strchr(oneline, '\n');
if (!eol)
eol = oneline + strlen(oneline);
- while (*oneline && isspace(*oneline) && *oneline != '\n')
- oneline++;
if (!prefixcmp(oneline, "[PATCH")) {
char *eob = strchr(oneline, ']');
if (eob && (!eol || eob < eol))
diff --git a/compat/snprintf.c b/compat/snprintf.c
new file mode 100644
index 0000000..dbfc2d6
--- /dev/null
+++ b/compat/snprintf.c
@@ -0,0 +1,40 @@
+#include "../git-compat-util.h"
+
+#undef vsnprintf
+int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
+{
+ char *s;
+ int ret;
+
+ ret = vsnprintf(str, maxsize, format, ap);
+ if (ret != -1)
+ return ret;
+
+ s = NULL;
+ if (maxsize < 128)
+ maxsize = 128;
+
+ while (ret == -1) {
+ maxsize *= 4;
+ str = realloc(s, maxsize);
+ if (! str)
+ break;
+ s = str;
+ ret = vsnprintf(str, maxsize, format, ap);
+ }
+ free(s);
+ return ret;
+}
+
+int git_snprintf(char *str, size_t maxsize, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = git_vsnprintf(str, maxsize, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
diff --git a/config.mak.in b/config.mak.in
index ee6c33d..8e1cd5f 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -46,3 +46,4 @@ NO_MKDTEMP=@NO_MKDTEMP@
NO_ICONV=@NO_ICONV@
OLD_ICONV=@OLD_ICONV@
NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
diff --git a/configure.ac b/configure.ac
index 85d7ef5..287149d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -326,6 +326,40 @@ else
NO_C99_FORMAT=
fi
AC_SUBST(NO_C99_FORMAT)
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+AC_CACHE_CHECK([whether snprintf() and/or vsnprintf() return bogus value],
+ [ac_cv_snprintf_returns_bogus],
+[
+AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+ #include "stdarg.h"
+
+ int test_vsnprintf(char *str, size_t maxsize, const char *format, ...)
+ {
+ int ret;
+ va_list ap;
+ va_start(ap, format);
+ ret = vsnprintf(str, maxsize, format, ap);
+ va_end(ap);
+ return ret;
+ }],
+ [[char buf[6];
+ if (test_vsnprintf(buf, 3, "%s", "12345") != 5
+ || strcmp(buf, "12")) return 1;
+ if (snprintf(buf, 3, "%s", "12345") != 5
+ || strcmp(buf, "12")) return 1]])],
+ [ac_cv_snprintf_returns_bogus=no],
+ [ac_cv_snprintf_returns_bogus=yes])
+])
+if test $ac_cv_snprintf_returns_bogus = yes; then
+ SNPRINTF_RETURNS_BOGUS=UnfortunatelyYes
+else
+ SNPRINTF_RETURNS_BOGUS=
+fi
+AC_SUBST(SNPRINTF_RETURNS_BOGUS)
## Checks for library functions.
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 49e6df0..848c067 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -70,7 +70,15 @@ __git_ps1 ()
local b
if [ -d "$g/../.dotest" ]
then
- r="|AM/REBASE"
+ if test -f "$g/../.dotest/rebasing"
+ then
+ r="|REBASE"
+ elif test -f "$g/../.dotest/applying"
+ then
+ r="|AM"
+ else
+ r="|AM/REBASE"
+ fi
b="$(git symbolic-ref HEAD 2>/dev/null)"
elif [ -f "$g/.dotest-merge/interactive" ]
then
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index c926823..4fa853f 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -1299,7 +1299,7 @@ Return the list of files that haven't been handled."
(let (author-name author-email subject date msg)
(with-temp-buffer
(let ((coding-system (git-get-logoutput-coding-system)))
- (git-call-process-env t nil "log" "-1" commit)
+ (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
(goto-char (point-min))
(when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
(setq author-name (match-string 1))
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 77c88eb..62a740c 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -567,7 +567,7 @@ generate_general_email()
echo ""
if [ "$newrev_type" = "commit" ]; then
echo $LOGBEGIN
- git show --no-color --root -s $newrev
+ git show --no-color --root -s --pretty=medium $newrev
echo $LOGEND
else
# What can we do here? The tag marks an object that is not
diff --git a/fast-import.c b/fast-import.c
index 7f197d5..655913d 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2291,7 +2291,8 @@ static void cmd_reset_branch(void)
else
b = new_branch(sp);
read_next_command();
- if (!cmd_from(b) && command_buf.len > 0)
+ cmd_from(b);
+ if (command_buf.len > 0)
unread_command_buf = 1;
}
diff --git a/fetch-pack.h b/fetch-pack.h
index 8d35ef6..8bd9c32 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -12,7 +12,8 @@ struct fetch_pack_args
use_thin_pack:1,
fetch_all:1,
verbose:1,
- no_progress:1;
+ no_progress:1,
+ include_tag:1;
};
struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/git-am.sh b/git-am.sh
index a2c6fea..1f6b5e0 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -9,7 +9,7 @@ git-am [options] <mbox>|<Maildir>...
git-am [options] --resolved
git-am [options] --skip
--
-d,dotest= use <dir> and not .dotest
+d,dotest= (removed -- do not use)
i,interactive run interactively
b,binary pass --allo-binary-replacement to git-apply
3,3way allow fall back on 3way merging if needed
@@ -21,9 +21,11 @@ C= pass it through git-apply
p= pass it through git-apply
resolvemsg= override error message when patch failure occurs
r,resolved to be used after a patch failure
-skip skip the current patch"
+skip skip the current patch
+rebasing (internal use for git-rebase)"
. git-sh-setup
+prefix=$(git rev-parse --show-prefix)
set_reflog_action am
require_work_tree
cd_to_toplevel
@@ -49,10 +51,6 @@ stop_here_user_resolve () {
then
cmdline="$cmdline -3"
fi
- if test '.dotest' != "$dotest"
- then
- cmdline="$cmdline -d=$dotest"
- fi
echo "When you have resolved this problem run \"$cmdline --resolved\"."
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
@@ -124,7 +122,8 @@ reread_subject () {
}
prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+dotest=".dotest"
+sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
resolvemsg= resume=
git_apply_opt=
@@ -149,8 +148,11 @@ do
resolved=t ;;
--skip)
skip=t ;;
+ --rebasing)
+ rebasing=t threeway=t keep=t binary=t ;;
-d|--dotest)
- shift; dotest=$1;;
+ die "-d option is no longer supported. Do not use."
+ ;;
--resolvemsg)
shift; resolvemsg=$1 ;;
--whitespace)
@@ -186,7 +188,7 @@ then
0,)
# No file input but without resume parameters; catch
# user error to feed us a patch from standard input
- # when there is already .dotest. This is somewhat
+ # when there is already $dotest. This is somewhat
# unreliable -- stdin could be /dev/null for example
# and the caller did not intend to feed us a patch but
# wanted to continue unattended.
@@ -206,6 +208,24 @@ else
# Start afresh.
mkdir -p "$dotest" || exit
+ if test -n "$prefix" && test $# != 0
+ then
+ first=t
+ for arg
+ do
+ test -n "$first" && {
+ set x
+ first=
+ }
+ case "$arg" in
+ /*)
+ set "$@" "$arg" ;;
+ *)
+ set "$@" "$prefix$arg" ;;
+ esac
+ done
+ shift
+ fi
git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
rm -fr "$dotest"
exit 1
@@ -220,6 +240,12 @@ else
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
echo 1 >"$dotest/next"
+ if test -n "$rebasing"
+ then
+ : >"$dotest/rebasing"
+ else
+ : >"$dotest/applying"
+ fi
fi
case "$resolved" in
diff --git a/git-compat-util.h b/git-compat-util.h
index 2a40703..73968e0 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -209,6 +209,15 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
extern FILE *git_fopen(const char*, const char*);
#endif
+#ifdef SNPRINTF_RETURNS_BOGUS
+#define snprintf git_snprintf
+extern int git_snprintf(char *str, size_t maxsize,
+ const char *format, ...);
+#define vsnprintf git_vsnprintf
+extern int git_vsnprintf(char *str, size_t maxsize,
+ const char *format, va_list ap);
+#endif
+
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL
@@ -437,4 +446,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
#define qsort git_qsort
#endif
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
#endif
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index afe3d0b..7f632af 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -2556,7 +2556,7 @@ sub update
if ($base) {
my @merged;
# print "want to log between $base $parent \n";
- open(GITLOG, '-|', 'git-log', "$base..$parent")
+ open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
or die "Cannot call git-log: $!";
my $mergedhash;
while (<GITLOG>) {
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 49e13f0..010353a 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -252,7 +252,16 @@ while read commit parents; do
git read-tree -i -m $commit
;;
*)
- git read-tree -i -m $commit:"$filter_subdir"
+ # The commit may not have the subdirectory at all
+ err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+ if ! git rev-parse --verify $commit:"$filter_subdir" 2>/dev/null
+ then
+ rm -f "$GIT_INDEX_FILE"
+ else
+ echo >&2 "$err"
+ false
+ fi
+ }
esac || die "Could not initialize the index"
GIT_COMMIT=$commit
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 01e0a46..4e32174 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -224,6 +224,11 @@ else
ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127)
MSGFMT := $(TCL_PATH) po/po2msg.sh
endif
+ ifeq (msgfmt,$(MSGFMT))
+ ifeq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null || echo $?),1)
+ MSGFMT := $(TCL_PATH) po/po2msg.sh
+ endif
+ endif
endif
msgsdir = $(gg_libdir)/msgs
diff --git a/git-merge.sh b/git-merge.sh
index 03cd398..7dbbb1d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -71,7 +71,7 @@ finish_up_to_date () {
squash_message () {
echo Squashed commit of the following:
echo
- git log --no-merges ^"$head" $remoteheads
+ git log --no-merges --pretty=medium ^"$head" $remoteheads
}
finish () {
diff --git a/git-mergetool.sh b/git-mergetool.sh
index cbbb707..5c86f69 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -34,7 +34,7 @@ base_present () {
cleanup_temp_files () {
if test "$1" = --save-backup ; then
- mv -- "$BACKUP" "$path.orig"
+ mv -- "$BACKUP" "$MERGED.orig"
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -67,14 +67,14 @@ resolve_symlink_merge () {
read ans
case "$ans" in
[lL]*)
- git checkout-index -f --stage=2 -- "$path"
- git add -- "$path"
+ git checkout-index -f --stage=2 -- "$MERGED"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
[rR]*)
- git checkout-index -f --stage=3 -- "$path"
- git add -- "$path"
+ git checkout-index -f --stage=3 -- "$MERGED"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
@@ -95,12 +95,12 @@ resolve_deleted_merge () {
read ans
case "$ans" in
[mMcC]*)
- git add -- "$path"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
[dD]*)
- git rm -- "$path" > /dev/null
+ git rm -- "$MERGED" > /dev/null
cleanup_temp_files
return
;;
@@ -112,11 +112,11 @@ resolve_deleted_merge () {
}
check_unchanged () {
- if test "$path" -nt "$BACKUP" ; then
+ if test "$MERGED" -nt "$BACKUP" ; then
status=0;
else
while true; do
- echo "$path seems unchanged."
+ echo "$MERGED seems unchanged."
printf "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
@@ -127,50 +127,38 @@ check_unchanged () {
fi
}
-save_backup () {
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
- fi
-}
-
-remove_backup () {
- if test "$status" -eq 0; then
- rm "$BACKUP"
- fi
-}
-
merge_file () {
- path="$1"
+ MERGED="$1"
- f=`git ls-files -u -- "$path"`
+ f=`git ls-files -u -- "$MERGED"`
if test -z "$f" ; then
- if test ! -f "$path" ; then
- echo "$path: file not found"
+ if test ! -f "$MERGED" ; then
+ echo "$MERGED: file not found"
else
- echo "$path: file does not need merging"
+ echo "$MERGED: file does not need merging"
fi
exit 1
fi
- ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
- BACKUP="$path.BACKUP.$ext"
- LOCAL="$path.LOCAL.$ext"
- REMOTE="$path.REMOTE.$ext"
- BASE="$path.BASE.$ext"
+ ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+ BACKUP="$MERGED.BACKUP.$ext"
+ LOCAL="$MERGED.LOCAL.$ext"
+ REMOTE="$MERGED.REMOTE.$ext"
+ BASE="$MERGED.BASE.$ext"
- mv -- "$path" "$BACKUP"
- cp -- "$BACKUP" "$path"
+ mv -- "$MERGED" "$BACKUP"
+ cp -- "$BACKUP" "$MERGED"
- base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
- local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
- remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+ base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
+ local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
+ remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
- base_present && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
- local_present && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
- remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
+ base_present && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
+ local_present && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
+ remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
if test -z "$local_mode" -o -z "$remote_mode"; then
- echo "Deleted merge conflict for '$path':"
+ echo "Deleted merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
@@ -178,14 +166,14 @@ merge_file () {
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
- echo "Symbolic link merge conflict for '$path':"
+ echo "Symbolic link merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
- echo "Normal merge conflict for '$path':"
+ echo "Normal merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
@@ -194,36 +182,32 @@ merge_file () {
case "$merge_tool" in
kdiff3)
if base_present ; then
- ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
- -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ ("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
+ -o "$MERGED" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
else
- ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
- -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ ("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
+ -o "$MERGED" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
- remove_backup
;;
tkdiff)
if base_present ; then
- "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -a "$BASE" -o "$MERGED" -- "$LOCAL" "$REMOTE"
else
- "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -o "$MERGED" -- "$LOCAL" "$REMOTE"
fi
status=$?
- save_backup
;;
meld|vimdiff)
touch "$BACKUP"
- "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
+ "$merge_tool_path" -- "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
- save_backup
;;
gvimdiff)
- touch "$BACKUP"
- "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
- check_unchanged
- save_backup
- ;;
+ touch "$BACKUP"
+ "$merge_tool_path" -f -- "$LOCAL" "$MERGED" "$REMOTE"
+ check_unchanged
+ ;;
xxdiff)
touch "$BACKUP"
if base_present ; then
@@ -231,53 +215,68 @@ merge_file () {
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
- --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+ --merged-file "$MERGED" -- "$LOCAL" "$BASE" "$REMOTE"
else
"$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" "$REMOTE"
+ --merged-file "$MERGED" -- "$LOCAL" "$REMOTE"
fi
check_unchanged
- save_backup
;;
opendiff)
touch "$BACKUP"
if base_present; then
- "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
else
- "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
fi
check_unchanged
- save_backup
;;
ecmerge)
touch "$BACKUP"
if base_present; then
- "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+ "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
else
- "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
+ "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
fi
check_unchanged
- save_backup
;;
emerge)
if base_present ; then
- "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
else
- "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
fi
status=$?
- save_backup
+ ;;
+ *)
+ if test -n "$merge_tool_cmd"; then
+ if test "$merge_tool_trust_exit_code" = "false"; then
+ touch "$BACKUP"
+ ( eval $merge_tool_cmd )
+ check_unchanged
+ else
+ ( eval $merge_tool_cmd )
+ status=$?
+ fi
+ fi
;;
esac
if test "$status" -ne 0; then
- echo "merge of $path failed" 1>&2
- mv -- "$BACKUP" "$path"
+ echo "merge of $MERGED failed" 1>&2
+ mv -- "$BACKUP" "$MERGED"
exit 1
fi
- git add -- "$path"
+
+ if test "$merge_keep_backup" = "true"; then
+ mv -- "$BACKUP" "$MERGED.orig"
+ else
+ rm -- "$BACKUP"
+ fi
+
+ git add -- "$MERGED"
cleanup_temp_files
}
@@ -309,12 +308,20 @@ do
shift
done
+valid_custom_tool()
+{
+ merge_tool_cmd="$(git config mergetool.$1.cmd)"
+ test -n "$merge_tool_cmd"
+}
+
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
- return 1
+ if ! valid_custom_tool "$1"; then
+ return 1
+ fi
;;
esac
}
@@ -380,10 +387,16 @@ else
init_merge_tool_path "$merge_tool"
- if ! type "$merge_tool_path" > /dev/null 2>&1; then
+ merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+
+ if test -z "$merge_tool_cmd" && ! 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
+
+ if ! test -z "$merge_tool_cmd"; then
+ merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
+ fi
fi
diff --git a/git-rebase.sh b/git-rebase.sh
index 6b9af96..452c5e7 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -376,7 +376,7 @@ fi
if test -z "$do_merge"
then
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
- git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
+ git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
ret=$?
test 0 != $ret -a -d .dotest &&
diff --git a/git-send-email.perl b/git-send-email.perl
index 29b1105..be4a20d 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -317,7 +317,7 @@ if ($suppress_cc{'all'}) {
# If explicit old-style ones are specified, they trump --suppress-cc.
$suppress_cc{'self'} = $suppress_from if defined $suppress_from;
-$suppress_cc{'sob'} = $signed_off_cc if defined $signed_off_cc;
+$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
# Debugging, print out the suppressions.
if (0) {
@@ -855,6 +855,7 @@ foreach my $t (@files) {
$message .= $_;
if (/^(Signed-off-by|Cc): (.*)$/i) {
next if ($suppress_cc{'sob'});
+ chomp;
my $c = $2;
chomp $c;
next if ($c eq $sender and $suppress_cc{'self'});
diff --git a/git-stash.sh b/git-stash.sh
index b00f888..c2b6820 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Copyright (c) 2007, Nanako Shiraishi
-USAGE='[ | save | list | show | apply | clear | create ]'
+USAGE='[ | save | list | show | apply | clear | drop | pop | create ]'
SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
@@ -196,6 +196,28 @@ apply_stash () {
fi
}
+drop_stash () {
+ have_stash || die 'No stash entries to drop'
+
+ if test $# = 0
+ then
+ set x "$ref_stash@{0}"
+ shift
+ fi
+ # Verify supplied argument looks like a stash entry
+ s=$(git rev-parse --revs-only --no-flags "$@") &&
+ git rev-parse --verify "$s:" > /dev/null 2>&1 &&
+ git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
+ git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
+ die "$*: not a valid stashed state"
+
+ git reflog delete --updateref --rewrite "$@" &&
+ echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+
+ # clear_stash if we just dropped the last stash entry
+ git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
+}
+
# Main command set
case "$1" in
list)
@@ -230,6 +252,18 @@ create)
fi
create_stash "$*" && echo "$w_commit"
;;
+drop)
+ shift
+ drop_stash "$@"
+ ;;
+pop)
+ shift
+ if apply_stash "$@"
+ then
+ test -z "$unstash_index" || shift
+ drop_stash "$@"
+ fi
+ ;;
*)
if test $# -eq 0
then
diff --git a/git-submodule.sh b/git-submodule.sh
index 67d3224..7171cb6 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -153,20 +153,6 @@ cmd_add()
usage
fi
- case "$repo" in
- ./*|../*)
- # dereference source url relative to parent's url
- realrepo="$(resolve_relative_url $repo)" ;;
- *)
- # Turn the source into an absolute path if
- # it is local
- if base=$(get_repo_base "$repo"); then
- repo="$base"
- fi
- realrepo=$repo
- ;;
- esac
-
# Guess path from repo if not specified or strip trailing slashes
if test -z "$path"; then
path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
@@ -174,15 +160,39 @@ cmd_add()
path=$(echo "$path" | sed -e 's|/*$||')
fi
- test -e "$path" &&
- die "'$path' already exists"
-
git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
die "'$path' already exists in the index"
- module_clone "$path" "$realrepo" || exit
- (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
- die "Unable to checkout submodule '$path'"
+ # perhaps the path exists and is already a git repo, else clone it
+ if test -e "$path"
+ then
+ if test -d "$path/.git" &&
+ test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+ then
+ echo "Adding existing repo at '$path' to the index"
+ else
+ die "'$path' already exists and is not a valid git repo"
+ fi
+ else
+ case "$repo" in
+ ./*|../*)
+ # dereference source url relative to parent's url
+ realrepo="$(resolve_relative_url $repo)" ;;
+ *)
+ # Turn the source into an absolute path if
+ # it is local
+ if base=$(get_repo_base "$repo"); then
+ repo="$base"
+ fi
+ realrepo=$repo
+ ;;
+ esac
+
+ module_clone "$path" "$realrepo" || exit
+ (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+ die "Unable to checkout submodule '$path'"
+ fi
+
git add "$path" ||
die "Failed to add submodule '$path'"
diff --git a/git-svn.perl b/git-svn.perl
index 9e2faf9..1195569 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -1540,9 +1540,14 @@ sub find_by_url { # repos_root and, path are optional
$remotes->{$repo_id}->{$_});
}
my $p = $path;
+ my $rwr = rewrite_root({repo_id => $repo_id});
unless (defined $p) {
$p = $full_url;
- $p =~ s#^\Q$u\E(?:/|$)## or next;
+ my $z = $u;
+ if ($rwr) {
+ $z = $rwr;
+ }
+ $p =~ s#^\Q$z\E(?:/|$)## or next;
}
foreach my $f (keys %$fetch) {
next if $f ne $p;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 922dee9..ec73cb1 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -5305,51 +5305,19 @@ sub git_search {
print "<table class=\"pickaxe search\">\n";
my $alternate = 1;
$/ = "\n";
- my $git_command = git_cmd_str();
- my $searchqtext = $searchtext;
- $searchqtext =~ s/'/'\\''/;
- my $pickaxe_flags = $search_use_regexp ? '--pickaxe-regex' : '';
- open my $fd, "-|", "$git_command rev-list $hash | " .
- "$git_command diff-tree -r --stdin -S\'$searchqtext\' $pickaxe_flags";
+ open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+ '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+ ($search_use_regexp ? '--pickaxe-regex' : ());
undef %co;
my @files;
while (my $line = <$fd>) {
- if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
- my %set;
- $set{'file'} = $6;
- $set{'from_id'} = $3;
- $set{'to_id'} = $4;
- $set{'id'} = $set{'to_id'};
- if ($set{'id'} =~ m/0{40}/) {
- $set{'id'} = $set{'from_id'};
- }
- if ($set{'id'} =~ m/0{40}/) {
- next;
- }
- push @files, \%set;
- } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
+ chomp $line;
+ next unless $line;
+
+ my %set = parse_difftree_raw_line($line);
+ if (defined $set{'commit'}) {
+ # finish previous commit
if (%co) {
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- 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>" . $author . "</i></td>\n" .
- "<td>" .
- $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
- -class => "list subject"},
- 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'},
- hash=>$set{'id'}, file_name=>$set{'file'}),
- -class => "list"},
- "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
- "<br/>\n";
- }
print "</td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
@@ -5358,11 +5326,44 @@ sub git_search {
print "</td>\n" .
"</tr>\n";
}
- %co = parse_commit($1);
+
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ %co = parse_commit($set{'commit'});
+ 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>$author</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+ -class => "list subject"},
+ chop_and_escape_str($co{'title'}, 50) . "<br/>");
+ } elsif (defined $set{'to_id'}) {
+ next if ($set{'to_id'} =~ m/^0{40}$/);
+
+ print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+ hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+ -class => "list"},
+ "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+ "<br/>\n";
}
}
close $fd;
+ # finish last commit (warning: repetition!)
+ if (%co) {
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+ " | " .
+ $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+ print "</td>\n" .
+ "</tr>\n";
+ }
+
print "</table>\n";
}
diff --git a/ident.c b/ident.c
index b839dcf..ed44a53 100644
--- a/ident.c
+++ b/ident.c
@@ -171,7 +171,7 @@ static const char au_env[] = "GIT_AUTHOR_NAME";
static const char co_env[] = "GIT_COMMITTER_NAME";
static const char *env_hint =
"\n"
-"*** Your name cannot be determined from your system services (gecos).\n"
+"*** Please tell me who you are.\n"
"\n"
"Run\n"
"\n"
diff --git a/path.c b/path.c
index af27161..f4ed979 100644
--- a/path.c
+++ b/path.c
@@ -283,7 +283,7 @@ int adjust_shared_perm(const char *path)
? (S_IXGRP|S_IXOTH)
: 0));
if (S_ISDIR(mode))
- mode |= S_ISGID;
+ mode |= FORCE_DIR_SET_GID;
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
return -2;
return 0;
diff --git a/quote.c b/quote.c
index e38ba39..d5cf9d8 100644
--- a/quote.c
+++ b/quote.c
@@ -330,7 +330,7 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
switch (*quoted++) {
case '"':
if (endp)
- *endp = quoted + 1;
+ *endp = quoted;
return 0;
case '\\':
break;
diff --git a/refs.c b/refs.c
index c979fb1..1b0050e 100644
--- a/refs.c
+++ b/refs.c
@@ -1033,7 +1033,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
return 1;
}
-static int close_ref(struct ref_lock *lock)
+int close_ref(struct ref_lock *lock)
{
if (close_lock_file(lock->lk))
return -1;
@@ -1041,7 +1041,7 @@ static int close_ref(struct ref_lock *lock)
return 0;
}
-static int commit_ref(struct ref_lock *lock)
+int commit_ref(struct ref_lock *lock)
{
if (commit_lock_file(lock->lk))
return -1;
diff --git a/refs.h b/refs.h
index 9cd16f8..06abee1 100644
--- a/refs.h
+++ b/refs.h
@@ -33,6 +33,12 @@ extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_
#define REF_NODEREF 0x01
extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
+/** Close the file descriptor owned by a lock and return the status */
+extern int close_ref(struct ref_lock *lock);
+
+/** Close and commit the ref locked by the lock */
+extern int commit_ref(struct ref_lock *lock);
+
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);
diff --git a/run-command.c b/run-command.c
index 743757c..44100a7 100644
--- a/run-command.c
+++ b/run-command.c
@@ -91,6 +91,13 @@ int start_command(struct child_process *cmd)
close(cmd->in);
}
+ if (cmd->no_stderr)
+ dup_devnull(2);
+ else if (need_err) {
+ dup2(fderr[1], 2);
+ close_pair(fderr);
+ }
+
if (cmd->no_stdout)
dup_devnull(1);
else if (cmd->stdout_to_stderr)
@@ -103,13 +110,6 @@ int start_command(struct child_process *cmd)
close(cmd->out);
}
- if (cmd->no_stderr)
- dup_devnull(2);
- else 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));
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index f959aae..24476be 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -175,6 +175,33 @@ test_expect_success 'recover and check' '
'
+test_expect_success 'delete' '
+ echo 1 > C &&
+ test_tick &&
+ git commit -m rat C &&
+
+ echo 2 > C &&
+ test_tick &&
+ git commit -m ox C &&
+
+ echo 3 > C &&
+ test_tick &&
+ git commit -m tiger C &&
+
+ test 5 = $(git reflog | wc -l) &&
+
+ git reflog delete master@{1} &&
+ git reflog show master > output &&
+ test 4 = $(wc -l < output) &&
+ ! grep ox < output &&
+
+ git reflog delete master@{07.04.2005.15:15:00.-0700} &&
+ git reflog show master > output &&
+ test 3 = $(wc -l < output) &&
+ ! grep dragon < output
+
+'
+
test_expect_success 'prune --expire' '
before=$(git count-objects | sed "s/ .*//") &&
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 9a9a250..aa282e1 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -40,8 +40,8 @@ test_expect_success 'parents of stash' '
test_expect_success 'apply needs clean working directory' '
echo 4 > other-file &&
git add other-file &&
- echo 5 > other-file
- ! git stash apply
+ echo 5 > other-file &&
+ test_must_fail git stash apply
'
test_expect_success 'apply stashed changes' '
@@ -70,7 +70,51 @@ test_expect_success 'unstashing in a subdirectory' '
git reset --hard HEAD &&
mkdir subdir &&
cd subdir &&
- git stash apply
+ git stash apply &&
+ cd ..
+'
+
+test_expect_success 'drop top stash' '
+ git reset --hard &&
+ git stash list > stashlist1 &&
+ echo 7 > file &&
+ git stash &&
+ git stash drop &&
+ git stash list > stashlist2 &&
+ diff stashlist1 stashlist2 &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'drop middle stash' '
+ git reset --hard &&
+ echo 8 > file &&
+ git stash &&
+ echo 9 > file &&
+ git stash &&
+ git stash drop stash@{1} &&
+ test 2 = $(git stash list | wc -l) &&
+ git stash apply &&
+ test 9 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file) &&
+ git reset --hard &&
+ git stash drop &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'stash pop' '
+ git reset --hard &&
+ git stash pop &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file) &&
+ test 0 = $(git stash list | wc -l)
'
test_done
diff --git a/t/t4150-am-subdir.sh b/t/t4150-am-subdir.sh
new file mode 100755
index 0000000..929d2cb
--- /dev/null
+++ b/t/t4150-am-subdir.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git am running from a subdirectory'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >world &&
+ git add world &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial &&
+ echo goodbye >world &&
+ git add world &&
+ test_tick &&
+ git commit -m second &&
+ git format-patch --stdout HEAD^ >patchfile &&
+ : >expect
+'
+
+test_expect_success 'am regularly from stdin' '
+ git checkout initial &&
+ git am <patchfile &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file' '
+ git checkout initial &&
+ git am patchfile &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from stdin in subdirectory' '
+ rm -fr subdir &&
+ git checkout initial &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am <../patchfile
+ ) &&
+ git diff master>actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory' '
+ rm -fr subdir &&
+ git checkout initial &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am ../patchfile
+ ) &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory with full path' '
+ rm -fr subdir &&
+ git checkout initial &&
+ P=$(pwd) &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am "$P/patchfile"
+ ) &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
new file mode 100755
index 0000000..0db2754
--- /dev/null
+++ b/t/t5305-include-tag.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git-pack-object --include-tag'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success setup '
+ echo c >d &&
+ git update-index --add d &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` &&
+ echo "object $commit" >sig &&
+ echo "type commit" >>sig &&
+ echo "tag mytag" >>sig &&
+ echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig &&
+ echo >>sig &&
+ echo "our test tag" >>sig &&
+ tag=`git mktag <sig` &&
+ rm d sig &&
+ git update-ref refs/tags/mytag $tag && {
+ echo $tree &&
+ echo $commit &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list
+'
+
+rm -rf clone.git
+test_expect_success 'pack without --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ test-1 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-1-${packname_1}.pack &&
+ git unpack-objects <test-1-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, no tag)' '
+ git rev-list --objects $commit >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ test_must_fail git cat-file -e $tag &&
+ git rev-list --objects $commit
+ ) >list.actual &&
+ git diff list.expect list.actual
+'
+
+rm -rf clone.git
+test_expect_success 'pack with --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ --include-tag \
+ test-2 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-2-${packname_1}.pack &&
+ git unpack-objects <test-2-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, have tag)' '
+ git rev-list --objects mytag >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git rev-list --objects $tag
+ ) >list.actual &&
+ git diff list.expect list.actual
+'
+
+test_done
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
new file mode 100755
index 0000000..86e5b9b
--- /dev/null
+++ b/t/t5503-tagfollow.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='test automatic tag following'
+
+. ./test-lib.sh
+
+# End state of the repository:
+#
+# T - tag1 S - tag2
+# / /
+# L - A ------ O ------ B
+# \ \ \
+# \ C - origin/cat \
+# origin/master master
+
+test_expect_success setup '
+ test_tick &&
+ echo ichi >file &&
+ git add file &&
+ git commit -m L &&
+ L=$(git rev-parse --verify HEAD) &&
+
+ (
+ mkdir cloned &&
+ cd cloned &&
+ git init-db &&
+ git remote add -f origin ..
+ ) &&
+
+ test_tick &&
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ A=$(git rev-parse --verify HEAD)
+'
+
+U=UPLOAD_LOG
+
+cat - <<EOF >expect
+#S
+want $A
+#E
+EOF
+test_expect_success 'fetch A (new commit : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $A = $(git rev-parse --verify origin/master)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_expect_success "create tag T on A, create C on branch cat" '
+ git tag -a -m tag1 tag1 $A &&
+ T=$(git rev-parse --verify tag1) &&
+
+ git checkout -b cat &&
+ echo C >file &&
+ git add file &&
+ git commit -m C &&
+ C=$(git rev-parse --verify HEAD) &&
+ git checkout master
+'
+
+cat - <<EOF >expect
+#S
+want $C
+want $T
+#E
+EOF
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $C = $(git rev-parse --verify origin/cat) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_expect_success "create commits O, B, tag S on B" '
+ test_tick &&
+ echo O >file &&
+ git add file &&
+ git commit -m O &&
+
+ test_tick &&
+ echo B >file &&
+ git add file &&
+ git commit -m B &&
+ B=$(git rev-parse --verify HEAD) &&
+
+ git tag -a -m tag2 tag2 $B &&
+ S=$(git rev-parse --verify tag2)
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $S = $(git rev-parse --verify tag2)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'new clone fetch master and tags' '
+ git branch -D cat
+ rm -f $U
+ (
+ mkdir clone2 &&
+ cd clone2 &&
+ git init &&
+ git remote add origin .. &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $S = $(git rev-parse --verify tag2) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 868babc..6e14bf1 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -179,4 +179,28 @@ test_expect_success 'Name needing quotes' '
'
+test_expect_success 'Subdirectory filter with disappearing trees' '
+ git reset --hard &&
+ git checkout master &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Adding foo" &&
+
+ git rm -r foo &&
+ test_tick &&
+ git commit -m "Removing foo" &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Re-adding foo" &&
+
+ git filter-branch -f --subdirectory-filter foo &&
+ test $(git rev-list master | wc -l) = 3
+'
+
test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100644
index 0000000..6b0483f
--- /dev/null
+++ b/t/t7610-mergetool.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git-mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo master >file1 &&
+ git add file1 &&
+ git commit -m "added file1" &&
+ git checkout -b branch1 master &&
+ echo branch1 change >file1 &&
+ echo branch1 newfile >file2 &&
+ git add file1 file2 &&
+ git commit -m "branch1 changes" &&
+ git checkout -b branch2 master &&
+ echo branch2 change >file1 &&
+ echo branch2 newfile >file2 &&
+ git add file1 file2 &&
+ git commit -m "branch2 changes" &&
+ git checkout master &&
+ echo master updated >file1 &&
+ echo master new >file2 &&
+ git add file1 file2 &&
+ git commit -m "master updates"
+'
+
+test_expect_success 'custom mergetool' '
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true &&
+ git checkout branch1 &&
+ ! git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+ test "$(cat file1)" = "master updated" &&
+ test "$(cat file2)" = "master new" &&
+ git commit -m "branch1 resolved with mergetool"
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index cceedbb..c4f4465 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -869,6 +869,8 @@ zcommits
COMMIT
reset refs/tags/O3-2nd
from :5
+reset refs/tags/O3-3rd
+from :5
INPUT_END
cat >expect <<INPUT_END
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 87a5ea4..6aea0ea 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -3,12 +3,16 @@
# Copyright (c) 2005 Junio C Hamano
#
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
# For repeatability, reset the environment to known value.
LANG=C
LC_ALL=C
PAGER=cat
TZ=UTC
-export LANG LC_ALL PAGER TZ
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
EDITOR=:
VISUAL=:
unset GIT_EDITOR
@@ -58,12 +62,14 @@ esac
# This test checks if command xyzzy does the right thing...
# '
# . ./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 &&
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+ TERM=$ORIGINAL_TERM &&
+ export TERM &&
+ [ -t 1 ] &&
+ tput bold >/dev/null 2>&1 &&
+ tput setaf 1 >/dev/null 2>&1 &&
+ tput sgr0 >/dev/null 2>&1
+ ) &&
color=t
while test "$#" -ne 0
@@ -91,6 +97,9 @@ done
if test -n "$color"; then
say_color () {
+ (
+ TERM=$ORIGINAL_TERM
+ export TERM
case "$1" in
error) tput bold; tput setaf 1;; # bold red
skip) tput bold; tput setaf 2;; # bold green
@@ -101,6 +110,7 @@ if test -n "$color"; then
shift
echo "* $*"
tput sgr0
+ )
}
else
say_color() {
diff --git a/transport.c b/transport.c
index 166c1d1..393e0e8 100644
--- a/transport.c
+++ b/transport.c
@@ -560,6 +560,7 @@ static int close_bundle(struct transport *transport)
struct git_transport_data {
unsigned thin : 1;
unsigned keep : 1;
+ unsigned followtags : 1;
int depth;
struct child_process *conn;
int fd[2];
@@ -580,6 +581,9 @@ static int set_git_option(struct transport *connection,
} else if (!strcmp(name, TRANS_OPT_THIN)) {
data->thin = !!value;
return 0;
+ } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
+ data->followtags = !!value;
+ return 0;
} else if (!strcmp(name, TRANS_OPT_KEEP)) {
data->keep = !!value;
return 0;
@@ -628,6 +632,7 @@ static int fetch_refs_via_pack(struct transport *transport,
args.keep_pack = data->keep;
args.lock_pack = 1;
args.use_thin_pack = data->thin;
+ args.include_tag = data->followtags;
args.verbose = transport->verbose > 0;
args.depth = data->depth;
diff --git a/transport.h b/transport.h
index 6fb4526..8abfc0a 100644
--- a/transport.h
+++ b/transport.h
@@ -53,6 +53,9 @@ struct transport *transport_get(struct remote *, const char *);
/* Limit the depth of the fetch if not null */
#define TRANS_OPT_DEPTH "depth"
+/* Aggressively fetch annotated tags if possible */
+#define TRANS_OPT_FOLLOWTAGS "followtags"
+
/**
* Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used.
diff --git a/upload-pack.c b/upload-pack.c
index e5421db..b46dd36 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -27,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
static unsigned long oldest_have;
static int multi_ack, nr_our_refs;
-static int use_thin_pack, use_ofs_delta, no_progress;
+static int use_thin_pack, use_ofs_delta, use_include_tag;
+static int no_progress;
static struct object_array have_obj;
static struct object_array want_obj;
static unsigned int timeout;
@@ -35,6 +36,7 @@ static unsigned int timeout;
* otherwise maximum packet size (up to 65520 bytes).
*/
static int use_sideband;
+static int debug_fd;
static void reset_timeout(void)
{
@@ -161,6 +163,8 @@ static void create_pack_file(void)
argv[arg++] = "--progress";
if (use_ofs_delta)
argv[arg++] = "--delta-base-offset";
+ if (use_include_tag)
+ argv[arg++] = "--include-tag";
argv[arg++] = NULL;
memset(&pack_objects, 0, sizeof(pack_objects));
@@ -444,6 +448,8 @@ static void receive_needs(void)
static char line[1000];
int len, depth = 0;
+ if (debug_fd)
+ write_in_full(debug_fd, "#S\n", 3);
for (;;) {
struct object *o;
unsigned char sha1_buf[20];
@@ -451,6 +457,8 @@ static void receive_needs(void)
reset_timeout();
if (!len)
break;
+ if (debug_fd)
+ write_in_full(debug_fd, line, len);
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
@@ -489,6 +497,8 @@ static void receive_needs(void)
use_sideband = DEFAULT_PACKET_MAX;
if (strstr(line+45, "no-progress"))
no_progress = 1;
+ if (strstr(line+45, "include-tag"))
+ use_include_tag = 1;
/* We have sent all our refs already, and the other end
* should have chosen out of them; otherwise they are
@@ -506,6 +516,8 @@ static void receive_needs(void)
add_object_array(o, NULL, &want_obj);
}
}
+ if (debug_fd)
+ write_in_full(debug_fd, "#E\n", 3);
if (depth == 0 && shallows.nr == 0)
return;
if (depth > 0) {
@@ -558,7 +570,8 @@ static void receive_needs(void)
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
- " side-band-64k ofs-delta shallow no-progress";
+ " side-band-64k ofs-delta shallow no-progress"
+ " include-tag";
struct object *o = parse_object(sha1);
if (!o)
@@ -631,6 +644,8 @@ int main(int argc, char **argv)
die("'%s': unable to chdir or not a git archive", dir);
if (is_repository_shallow())
die("attempt to fetch/clone from a shallow repository");
+ if (getenv("GIT_DEBUG_SEND_PACK"))
+ debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
upload_pack();
return 0;
}