summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/1.9.2.txt47
-rw-r--r--Documentation/git-http-backend.txt2
-rw-r--r--Documentation/merge-strategies.txt4
-rw-r--r--Makefile8
l---------RelNotes2
-rw-r--r--builtin/checkout.c8
-rw-r--r--builtin/clone.c4
-rw-r--r--builtin/commit.c35
-rw-r--r--builtin/gc.c2
-rw-r--r--builtin/index-pack.c4
-rw-r--r--builtin/merge.c6
-rw-r--r--builtin/mv.c9
-rw-r--r--builtin/receive-pack.c16
-rw-r--r--builtin/update-ref.c1
-rw-r--r--commit.h5
-rw-r--r--compat/gmtime.c29
-rw-r--r--config.mak.uname1
-rw-r--r--diff-no-index.c2
-rw-r--r--entry.c64
-rw-r--r--fetch-pack.c11
-rw-r--r--git-compat-util.h7
-rw-r--r--git-rebase--interactive.sh2
-rwxr-xr-xgit-stash.sh10
-rw-r--r--remote.c50
-rw-r--r--run-command.c44
-rw-r--r--run-command.h7
-rw-r--r--shallow.c69
-rw-r--r--t/lib-terminal.sh37
-rwxr-xr-xt/t1400-update-ref.sh11
-rwxr-xr-xt/t4212-log-corrupt.sh6
-rwxr-xr-xt/t5510-fetch.sh20
-rwxr-xr-xt/t7001-mv.sh13
-rwxr-xr-xt/t7060-wtstatus.sh14
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh33
-rwxr-xr-xt/t7512-status-help.sh12
-rwxr-xr-xt/t7514-commit-patch.sh34
-rw-r--r--t/test-lib.sh4
-rw-r--r--upload-pack.c7
-rw-r--r--wt-status.c130
-rw-r--r--wt-status.h1
40 files changed, 543 insertions, 228 deletions
diff --git a/Documentation/RelNotes/1.9.2.txt b/Documentation/RelNotes/1.9.2.txt
new file mode 100644
index 0000000000..35904d0458
--- /dev/null
+++ b/Documentation/RelNotes/1.9.2.txt
@@ -0,0 +1,47 @@
+Git v1.9.2 Release Notes
+========================
+
+Fixes since v1.9.1
+------------------
+
+ * When it is not necessary to edit a commit log message (e.g. "git
+ commit -m" is given a message without specifying "-e"), we used to
+ disable the spawning of the editor by overriding GIT_EDITOR, but
+ this means all the uses of the editor, other than to edit the
+ commit log message, are also affected.
+
+ * "git status --porcelain --branch" showed its output with labels
+ "ahead/behind/gone" translated to the user's locale.
+
+ * "git mv" that moves a submodule forgot to adjust the array that
+ uses to keep track of which submodules were to be moved to update
+ its configuration.
+
+ * Length limit for the pathname used when removing a path in a deep
+ subdirectory has been removed to avoid buffer overflows.
+
+ * The test helper lib-terminal always run an actual test_expect_*
+ when included, which screwed up with the use of skil-all that may
+ have to be done later.
+
+ * "git index-pack" used a wrong variable to name the keep-file in an
+ error message when the file cannot be written or closed.
+
+ * "rebase -i" produced a broken insn sheet when the title of a commit
+ happened to contain '\n' (or ended with '\c') due to a careless use
+ of 'echo'.
+
+ * There were a few instances of 'git-foo' remaining in the
+ documentation that should have been spelled 'git foo'.
+
+ * Serving objects from a shallow repository needs to write a
+ new file to hold the temporary shallow boundaries but it was not
+ cleaned when we exit due to die() or a signal.
+
+ * When "git stash pop" stops after failing to apply the stash
+ (e.g. due to conflicting changes), the stash is not dropped. State
+ that explicitly in the output to let the users know.
+
+ * The labels in "git status" output that describe the nature of
+ conflicts (e.g. "both deleted") were limited to 20 bytes, which was
+ too short for some l10n (e.g. fr).
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index e8c13f60ae..d422ba4b59 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -191,7 +191,7 @@ ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
----------------------------------------------------------------
Lighttpd::
- Ensure that `mod_cgi`, `mod_alias, `mod_auth`, `mod_setenv` are
+ Ensure that `mod_cgi`, `mod_alias`, `mod_auth`, `mod_setenv` are
loaded, then set `GIT_PROJECT_ROOT` appropriately and redirect
all requests to the CGI:
+
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index fb6e593e7c..feabc1ccf4 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -1,10 +1,10 @@
MERGE STRATEGIES
----------------
-The merge mechanism ('git-merge' and 'git-pull' commands) allows the
+The merge mechanism (`git merge` and `git pull` commands) allows the
backend 'merge strategies' to be chosen with `-s` option. Some strategies
can also take their own options, which can be passed by giving `-X<option>`
-arguments to 'git-merge' and/or 'git-pull'.
+arguments to `git merge` and/or `git pull`.
resolve::
This can only resolve two heads (i.e. the current branch
diff --git a/Makefile b/Makefile
index fc02788184..33aa15b0e3 100644
--- a/Makefile
+++ b/Makefile
@@ -342,6 +342,9 @@ all::
# Define DEFAULT_HELP_FORMAT to "man", "info" or "html"
# (defaults to "man") if you want to have a different default when
# "git help" is called without a parameter specifying the format.
+#
+# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
+# return NULL when it receives a bogus time_t.
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -1494,6 +1497,11 @@ ifneq (,$(XDL_FAST_HASH))
BASIC_CFLAGS += -DXDL_FAST_HASH
endif
+ifdef GMTIME_UNRELIABLE_ERRORS
+ COMPAT_OBJS += compat/gmtime.o
+ BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
+endif
+
ifeq ($(TCLTK_PATH),)
NO_TCLTK = NoThanks
endif
diff --git a/RelNotes b/RelNotes
index 0e72d3b564..58c32c3525 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.9.1.txt \ No newline at end of file
+Documentation/RelNotes/1.9.2.txt \ No newline at end of file
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 7f37d1a245..63151e08a4 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,10 +53,10 @@ struct checkout_opts {
static int post_checkout_hook(struct commit *old, struct commit *new,
int changed)
{
- return run_hook(NULL, "post-checkout",
- sha1_to_hex(old ? old->object.sha1 : null_sha1),
- sha1_to_hex(new ? new->object.sha1 : null_sha1),
- changed ? "1" : "0", NULL);
+ return run_hook_le(NULL, "post-checkout",
+ sha1_to_hex(old ? old->object.sha1 : null_sha1),
+ sha1_to_hex(new ? new->object.sha1 : null_sha1),
+ changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
diff --git a/builtin/clone.c b/builtin/clone.c
index 43e772ccdb..9b3c04d914 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -660,8 +660,8 @@ static int checkout(void)
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
- sha1_to_hex(sha1), "1", NULL);
+ err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
+ sha1_to_hex(sha1), "1", NULL);
if (!err && option_recursive)
err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
diff --git a/builtin/commit.c b/builtin/commit.c
index 5d594a4743..7d711eafa6 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -612,7 +612,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
/* This checks and barfs if author is badly specified */
determine_author_info(author_ident);
- if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+ if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
return 0;
if (squash_message) {
@@ -866,8 +866,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- if (run_hook(index_file, "prepare-commit-msg",
- git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+ if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+ git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
return 0;
if (use_editor) {
@@ -883,7 +883,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (!no_verify &&
- run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+ run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
return 0;
}
@@ -1067,8 +1067,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
use_editor = 0;
if (0 <= edit_flag)
use_editor = edit_flag;
- if (!use_editor)
- setenv("GIT_EDITOR", ":", 1);
/* Sanity check options */
if (amend && !current_head)
@@ -1445,6 +1443,29 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
return finish_command(&proc);
}
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
+{
+ const char *hook_env[3] = { NULL };
+ char index[PATH_MAX];
+ va_list args;
+ int ret;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ hook_env[0] = index;
+
+ /*
+ * Let the hook know that no editor will be launched.
+ */
+ if (!editor_is_used)
+ hook_env[1] = "GIT_EDITOR=:";
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env, name, args);
+ va_end(args);
+
+ return ret;
+}
+
int cmd_commit(int argc, const char **argv, const char *prefix)
{
static struct wt_status s;
@@ -1669,7 +1690,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
- run_hook(get_index_file(), "post-commit", NULL);
+ run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
struct notes_rewrite_cfg *cfg;
cfg = init_copy_notes_for_rewrite("amend");
diff --git a/builtin/gc.c b/builtin/gc.c
index c19545d49e..7fa717a3b6 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -179,7 +179,7 @@ static int need_to_gc(void)
else if (!too_many_loose_objects())
return 0;
- if (run_hook(NULL, "pre-auto-gc", NULL))
+ if (run_hook_le(NULL, "pre-auto-gc", NULL))
return 0;
return 1;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 2f37a38fbc..fa74972886 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1291,7 +1291,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
if (keep_fd < 0) {
if (errno != EEXIST)
die_errno(_("cannot write keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
} else {
if (keep_msg_len > 0) {
write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1299,7 +1299,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (close(keep_fd) != 0)
die_errno(_("cannot close written keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
report = "keep";
}
}
diff --git a/builtin/merge.c b/builtin/merge.c
index e576a7fdc6..b11a528ce5 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -421,7 +421,7 @@ static void finish(struct commit *head_commit,
}
/* Run a post-merge hook */
- run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
+ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
strbuf_release(&reflog_message);
}
@@ -821,8 +821,8 @@ static void prepare_to_commit(struct commit_list *remoteheads)
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
write_merge_msg(&msg);
- if (run_hook(get_index_file(), "prepare-commit-msg",
- git_path("MERGE_MSG"), "merge", NULL, NULL))
+ if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
+ git_path("MERGE_MSG"), "merge", NULL))
abort_commit(remoteheads, NULL);
if (0 < option_edit) {
if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
diff --git a/builtin/mv.c b/builtin/mv.c
index 21c46d1636..45e57f307b 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -179,6 +179,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
modes = xrealloc(modes,
(argc + last - first)
* sizeof(enum update_mode));
+ submodule_gitfile = xrealloc(submodule_gitfile,
+ (argc + last - first)
+ * sizeof(char *));
}
dst = add_slash(dst);
@@ -192,6 +195,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
prefix_path(dst, dst_len,
path + length + 1);
modes[argc + j] = INDEX;
+ submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
}
@@ -227,6 +231,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
memmove(destination + i,
destination + i + 1,
(argc - i) * sizeof(char *));
+ memmove(modes + i, modes + i + 1,
+ (argc - i) * sizeof(enum update_mode));
+ memmove(submodule_gitfile + i,
+ submodule_gitfile + i + 1,
+ (argc - i) * sizeof(char *));
i--;
}
} else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 85bba356fa..c3230817db 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -828,14 +828,10 @@ static void execute_commands(struct command *commands,
}
}
- if (shallow_update) {
- if (!checked_connectivity)
- error("BUG: run 'git fsck' for safety.\n"
- "If there are errors, try to remove "
- "the reported refs above");
- if (alt_shallow_file && *alt_shallow_file)
- unlink(alt_shallow_file);
- }
+ if (shallow_update && !checked_connectivity)
+ error("BUG: run 'git fsck' for safety.\n"
+ "If there are errors, try to remove "
+ "the reported refs above");
}
static struct command *read_head_info(struct sha1_array *shallow)
@@ -1087,10 +1083,6 @@ static void update_shallow_info(struct command *commands,
cmd->skip_update = 1;
}
}
- if (alt_shallow_file && *alt_shallow_file) {
- unlink(alt_shallow_file);
- alt_shallow_file = NULL;
- }
free(ref_status);
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 1292cfea11..5c208bb1fc 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -147,6 +147,7 @@ static void parse_cmd_create(const char *next)
struct ref_update *update;
update = update_alloc();
+ update->have_old = 1;
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
update_store_ref_name(update, ref.buf);
diff --git a/commit.h b/commit.h
index 16d9c43513..a9f177ba48 100644
--- a/commit.h
+++ b/commit.h
@@ -209,7 +209,7 @@ extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
extern void setup_alternate_shallow(struct lock_file *shallow_lock,
const char **alternate_shallow_file,
const struct sha1_array *extra);
-extern char *setup_temporary_shallow(const struct sha1_array *extra);
+extern const char *setup_temporary_shallow(const struct sha1_array *extra);
extern void advertise_shallow_grafts(int);
struct shallow_info {
@@ -304,4 +304,7 @@ extern void check_commit_signature(const struct commit* commit, struct signature
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+LAST_ARG_MUST_BE_NULL
+extern int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
+
#endif /* COMMIT_H */
diff --git a/compat/gmtime.c b/compat/gmtime.c
new file mode 100644
index 0000000000..e8362dd2b9
--- /dev/null
+++ b/compat/gmtime.c
@@ -0,0 +1,29 @@
+#include "../git-compat-util.h"
+#undef gmtime
+#undef gmtime_r
+
+struct tm *git_gmtime(const time_t *timep)
+{
+ static struct tm result;
+ return git_gmtime_r(timep, &result);
+}
+
+struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *ret;
+
+ memset(result, 0, sizeof(*result));
+ ret = gmtime_r(timep, result);
+
+ /*
+ * Rather than NULL, FreeBSD gmtime simply leaves the "struct tm"
+ * untouched when it encounters overflow. Since "mday" cannot otherwise
+ * be zero, we can test this very quickly.
+ */
+ if (ret && !ret->tm_mday) {
+ ret = NULL;
+ errno = EOVERFLOW;
+ }
+
+ return ret;
+}
diff --git a/config.mak.uname b/config.mak.uname
index 7d31fad241..efaed94d5d 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -188,6 +188,7 @@ ifeq ($(uname_S),FreeBSD)
endif
PYTHON_PATH = /usr/local/bin/python
HAVE_PATHS_H = YesPlease
+ GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
diff --git a/diff-no-index.c b/diff-no-index.c
index 33e5982a1c..798477f7e5 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -202,7 +202,7 @@ void diff_no_index(struct rev_info *revs,
i++;
else {
j = diff_opt_parse(&revs->diffopt, argv + i, argc - i);
- if (!j)
+ if (j <= 0)
die("invalid diff option/value: %s", argv[i]);
i += j;
}
diff --git a/entry.c b/entry.c
index 7b7aa8167a..77c6882624 100644
--- a/entry.c
+++ b/entry.c
@@ -44,33 +44,33 @@ static void create_directories(const char *path, int path_len,
free(buf);
}
-static void remove_subtree(const char *path)
+static void remove_subtree(struct strbuf *path)
{
- DIR *dir = opendir(path);
+ DIR *dir = opendir(path->buf);
struct dirent *de;
- char pathbuf[PATH_MAX];
- char *name;
+ int origlen = path->len;
if (!dir)
- die_errno("cannot opendir '%s'", path);
- strcpy(pathbuf, path);
- name = pathbuf + strlen(path);
- *name++ = '/';
+ die_errno("cannot opendir '%s'", path->buf);
while ((de = readdir(dir)) != NULL) {
struct stat st;
+
if (is_dot_or_dotdot(de->d_name))
continue;
- strcpy(name, de->d_name);
- if (lstat(pathbuf, &st))
- die_errno("cannot lstat '%s'", pathbuf);
+
+ strbuf_addch(path, '/');
+ strbuf_addstr(path, de->d_name);
+ if (lstat(path->buf, &st))
+ die_errno("cannot lstat '%s'", path->buf);
if (S_ISDIR(st.st_mode))
- remove_subtree(pathbuf);
- else if (unlink(pathbuf))
- die_errno("cannot unlink '%s'", pathbuf);
+ remove_subtree(path);
+ else if (unlink(path->buf))
+ die_errno("cannot unlink '%s'", path->buf);
+ strbuf_setlen(path, origlen);
}
closedir(dir);
- if (rmdir(path))
- die_errno("cannot rmdir '%s'", path);
+ if (rmdir(path->buf))
+ die_errno("cannot rmdir '%s'", path->buf);
}
static int create_file(const char *path, unsigned int mode)
@@ -245,27 +245,25 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
int checkout_entry(struct cache_entry *ce,
const struct checkout *state, char *topath)
{
- static struct strbuf path_buf = STRBUF_INIT;
- char *path;
+ static struct strbuf path = STRBUF_INIT;
struct stat st;
- int len;
if (topath)
return write_entry(ce, topath, state, 1);
- strbuf_reset(&path_buf);
- strbuf_add(&path_buf, state->base_dir, state->base_dir_len);
- strbuf_add(&path_buf, ce->name, ce_namelen(ce));
- path = path_buf.buf;
- len = path_buf.len;
+ strbuf_reset(&path);
+ strbuf_add(&path, state->base_dir, state->base_dir_len);
+ strbuf_add(&path, ce->name, ce_namelen(ce));
- if (!check_path(path, len, &st, state->base_dir_len)) {
+ if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
if (!changed)
return 0;
if (!state->force) {
if (!state->quiet)
- fprintf(stderr, "%s already exists, no checkout\n", path);
+ fprintf(stderr,
+ "%s already exists, no checkout\n",
+ path.buf);
return -1;
}
@@ -280,12 +278,14 @@ int checkout_entry(struct cache_entry *ce,
if (S_ISGITLINK(ce->ce_mode))
return 0;
if (!state->force)
- return error("%s is a directory", path);
- remove_subtree(path);
- } else if (unlink(path))
- return error("unable to unlink old '%s' (%s)", path, strerror(errno));
+ return error("%s is a directory", path.buf);
+ remove_subtree(&path);
+ } else if (unlink(path.buf))
+ return error("unable to unlink old '%s' (%s)",
+ path.buf, strerror(errno));
} else if (state->not_new)
return 0;
- create_directories(path, len, state);
- return write_entry(ce, path, state, 0);
+
+ create_directories(path.buf, path.len, state);
+ return write_entry(ce, path.buf, state, 0);
}
diff --git a/fetch-pack.c b/fetch-pack.c
index f061f1fe85..90d47da8a9 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -948,17 +948,6 @@ static void update_shallow(struct fetch_pack_args *args,
if (!si->shallow || !si->shallow->nr)
return;
- if (alternate_shallow_file) {
- /*
- * The temporary shallow file is only useful for
- * index-pack and unpack-objects because it may
- * contain more roots than we want. Delete it.
- */
- if (*alternate_shallow_file)
- unlink(alternate_shallow_file);
- free((char *)alternate_shallow_file);
- }
-
if (args->cloning) {
/*
* remote is shallow, but this is a clone, there are
diff --git a/git-compat-util.h b/git-compat-util.h
index cbd86c37f5..d493a8c67a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -721,4 +721,11 @@ void warn_on_inaccessible(const char *path);
/* Get the passwd entry for the UID of the current process. */
struct passwd *xgetpwuid_self(void);
+#ifdef GMTIME_UNRELIABLE_ERRORS
+struct tm *git_gmtime(const time_t *);
+struct tm *git_gmtime_r(const time_t *, struct tm *);
+#define gmtime git_gmtime
+#define gmtime_r git_gmtime_r
+#endif
+
#endif
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 43c19e0829..43631b4723 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -739,7 +739,7 @@ rearrange_squash () {
;;
esac
done
- echo "$sha1 $action $prefix $rest"
+ printf '%s %s %s %s\n' "$sha1" "$action" "$prefix" "$rest"
# if it's a single word, try to resolve to a full sha1 and
# emit a second copy. This allows us to match on both message
# and on sha1 prefix
diff --git a/git-stash.sh b/git-stash.sh
index f0a94abf14..4798bcf0e5 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -512,8 +512,14 @@ apply_stash () {
pop_stash() {
assert_stash_ref "$@"
- apply_stash "$@" &&
- drop_stash "$@"
+ if apply_stash "$@"
+ then
+ drop_stash "$@"
+ else
+ status=$?
+ say "The stash is kept in case you need it again."
+ exit $status
+ fi
}
drop_stash () {
diff --git a/remote.c b/remote.c
index f43c7a9d53..ebed40dcb6 100644
--- a/remote.c
+++ b/remote.c
@@ -857,6 +857,32 @@ static int match_name_with_pattern(const char *key, const char *name,
return ret;
}
+static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)
+{
+ int i;
+ int find_src = !query->src;
+
+ if (find_src && !query->dst)
+ error("query_refspecs_multiple: need either src or dst");
+
+ for (i = 0; i < ref_count; i++) {
+ struct refspec *refspec = &refs[i];
+ const char *key = find_src ? refspec->dst : refspec->src;
+ const char *value = find_src ? refspec->src : refspec->dst;
+ const char *needle = find_src ? query->dst : query->src;
+ char **result = find_src ? &query->src : &query->dst;
+
+ if (!refspec->dst)
+ continue;
+ if (refspec->pattern) {
+ if (match_name_with_pattern(key, needle, value, result))
+ string_list_append_nodup(results, *result);
+ } else if (!strcmp(needle, key)) {
+ string_list_append(results, value);
+ }
+ }
+}
+
int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
{
int i;
@@ -1997,25 +2023,37 @@ static int get_stale_heads_cb(const char *refname,
const unsigned char *sha1, int flags, void *cb_data)
{
struct stale_heads_info *info = cb_data;
+ struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec query;
+ int i, stale = 1;
memset(&query, 0, sizeof(struct refspec));
query.dst = (char *)refname;
- if (query_refspecs(info->refs, info->ref_count, &query))
- return 0; /* No matches */
+ query_refspecs_multiple(info->refs, info->ref_count, &query, &matches);
+ if (matches.nr == 0)
+ goto clean_exit; /* No matches */
/*
* If we did find a suitable refspec and it's not a symref and
* it's not in the list of refs that currently exist in that
- * remote we consider it to be stale.
+ * remote, we consider it to be stale. In order to deal with
+ * overlapping refspecs, we need to go over all of the
+ * matching refs.
*/
- if (!((flags & REF_ISSYMREF) ||
- string_list_has_string(info->ref_names, query.src))) {
+ if (flags & REF_ISSYMREF)
+ goto clean_exit;
+
+ for (i = 0; stale && i < matches.nr; i++)
+ if (string_list_has_string(info->ref_names, matches.items[i].string))
+ stale = 0;
+
+ if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
hashcpy(ref->new_sha1, sha1);
}
- free(query.src);
+clean_exit:
+ string_list_clear(&matches, 0);
return 0;
}
diff --git a/run-command.c b/run-command.c
index 3914d9c511..75abc478c6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -760,13 +760,11 @@ char *find_hook(const char *name)
return path;
}
-int run_hook(const char *index_file, const char *name, ...)
+int run_hook_ve(const char *const *env, const char *name, va_list args)
{
struct child_process hook;
struct argv_array argv = ARGV_ARRAY_INIT;
- const char *p, *env[2];
- char index[PATH_MAX];
- va_list args;
+ const char *p;
int ret;
p = find_hook(name);
@@ -775,23 +773,45 @@ int run_hook(const char *index_file, const char *name, ...)
argv_array_push(&argv, p);
- va_start(args, name);
while ((p = va_arg(args, const char *)))
argv_array_push(&argv, p);
- va_end(args);
memset(&hook, 0, sizeof(hook));
hook.argv = argv.argv;
+ hook.env = env;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
- if (index_file) {
- snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
- env[0] = index;
- env[1] = NULL;
- hook.env = env;
- }
ret = run_command(&hook);
argv_array_clear(&argv);
return ret;
}
+
+int run_hook_le(const char *const *env, const char *name, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, name);
+ ret = run_hook_ve(env, name, args);
+ va_end(args);
+
+ return ret;
+}
+
+int run_hook_with_custom_index(const char *index_file, const char *name, ...)
+{
+ const char *hook_env[3] = { NULL };
+ char index[PATH_MAX];
+ va_list args;
+ int ret;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ hook_env[0] = index;
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env, name, args);
+ va_end(args);
+
+ return ret;
+}
diff --git a/run-command.h b/run-command.h
index 6b985afd07..3653bfa6e1 100644
--- a/run-command.h
+++ b/run-command.h
@@ -47,7 +47,12 @@ int run_command(struct child_process *);
extern char *find_hook(const char *name);
LAST_ARG_MUST_BE_NULL
-extern int run_hook(const char *index_file, const char *name, ...);
+extern int run_hook_le(const char *const *env, const char *name, ...);
+extern int run_hook_ve(const char *const *env, const char *name, va_list args);
+
+LAST_ARG_MUST_BE_NULL
+__attribute__((deprecated))
+extern int run_hook_with_custom_index(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
diff --git a/shallow.c b/shallow.c
index bbc98b55c0..0b267b6411 100644
--- a/shallow.c
+++ b/shallow.c
@@ -8,9 +8,10 @@
#include "diff.h"
#include "revision.h"
#include "commit-slab.h"
+#include "sigchain.h"
static int is_shallow = -1;
-static struct stat shallow_stat;
+static struct stat_validity shallow_stat;
static char *alternate_shallow_file;
void set_alternate_shallow_file(const char *path, int override)
@@ -52,12 +53,12 @@ int is_repository_shallow(void)
* shallow file should be used. We could just open it and it
* will likely fail. But let's do an explicit check instead.
*/
- if (!*path ||
- stat(path, &shallow_stat) ||
- (fp = fopen(path, "r")) == NULL) {
+ if (!*path || (fp = fopen(path, "r")) == NULL) {
+ stat_validity_clear(&shallow_stat);
is_shallow = 0;
return is_shallow;
}
+ stat_validity_update(&shallow_stat, fileno(fp));
is_shallow = 1;
while (fgets(buf, sizeof(buf), fp)) {
@@ -137,21 +138,11 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
void check_shallow_file_for_update(void)
{
- struct stat st;
-
- if (!is_shallow)
- return;
- else if (is_shallow == -1)
+ if (is_shallow == -1)
die("BUG: shallow must be initialized by now");
- if (stat(git_path("shallow"), &st))
- die("shallow file was removed during fetch");
- else if (st.st_mtime != shallow_stat.st_mtime
-#ifdef USE_NSEC
- || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
-#endif
- )
- die("shallow file was changed during fetch");
+ if (!stat_validity_check(&shallow_stat, git_path("shallow")))
+ die("shallow file has changed since we read it");
}
#define SEEN_ONLY 1
@@ -216,27 +207,53 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
}
-char *setup_temporary_shallow(const struct sha1_array *extra)
+static struct strbuf temporary_shallow = STRBUF_INIT;
+
+static void remove_temporary_shallow(void)
+{
+ if (temporary_shallow.len) {
+ unlink_or_warn(temporary_shallow.buf);
+ strbuf_reset(&temporary_shallow);
+ }
+}
+
+static void remove_temporary_shallow_on_signal(int signo)
+{
+ remove_temporary_shallow();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
+const char *setup_temporary_shallow(const struct sha1_array *extra)
{
+ static int installed_handler;
struct strbuf sb = STRBUF_INIT;
int fd;
+ if (temporary_shallow.len)
+ die("BUG: attempt to create two temporary shallow files");
+
if (write_shallow_commits(&sb, 0, extra)) {
- struct strbuf path = STRBUF_INIT;
- strbuf_addstr(&path, git_path("shallow_XXXXXX"));
- fd = xmkstemp(path.buf);
+ strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
+ fd = xmkstemp(temporary_shallow.buf);
+
+ if (!installed_handler) {
+ atexit(remove_temporary_shallow);
+ sigchain_push_common(remove_temporary_shallow_on_signal);
+ }
+
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
- path.buf);
+ temporary_shallow.buf);
close(fd);
strbuf_release(&sb);
- return strbuf_detach(&path, NULL);
+ return temporary_shallow.buf;
}
/*
* is_repository_shallow() sees empty string as "no shallow
* file".
*/
- return xstrdup("");
+ return temporary_shallow.buf;
}
void setup_alternate_shallow(struct lock_file *shallow_lock,
@@ -246,9 +263,9 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
struct strbuf sb = STRBUF_INIT;
int fd;
- check_shallow_file_for_update();
fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
LOCK_DIE_ON_ERROR);
+ check_shallow_file_for_update();
if (write_shallow_commits(&sb, 0, extra)) {
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
@@ -293,9 +310,9 @@ void prune_shallow(int show_only)
strbuf_release(&sb);
return;
}
- check_shallow_file_for_update();
fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
LOCK_DIE_ON_ERROR);
+ check_shallow_file_for_update();
if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh
index 9a2dca506a..51845491bb 100644
--- a/t/lib-terminal.sh
+++ b/t/lib-terminal.sh
@@ -1,6 +1,20 @@
# Helpers for terminal output tests.
-test_expect_success PERL 'set up terminal for tests' '
+# Catch tests which should depend on TTY but forgot to. There's no need
+# to aditionally check that the TTY prereq is set here. If the test declared
+# it and we are running the test, then it must have been set.
+test_terminal () {
+ if ! test_declared_prereq TTY
+ then
+ echo >&4 "test_terminal: need to declare TTY prerequisite"
+ return 127
+ fi
+ perl "$TEST_DIRECTORY"/test-terminal.perl "$@"
+}
+
+test_lazy_prereq TTY '
+ test_have_prereq PERL &&
+
# Reading from the pty master seems to get stuck _sometimes_
# on Mac OS X 10.5.0, using Perl 5.10.0 or 5.8.9.
#
@@ -15,21 +29,8 @@ test_expect_success PERL 'set up terminal for tests' '
# After 2000 iterations or so it hangs.
# https://rt.cpan.org/Ticket/Display.html?id=65692
#
- if test "$(uname -s)" = Darwin
- then
- :
- elif
- perl "$TEST_DIRECTORY"/test-terminal.perl \
- sh -c "test -t 1 && test -t 2"
- then
- test_set_prereq TTY &&
- test_terminal () {
- if ! test_declared_prereq TTY
- then
- echo >&4 "test_terminal: need to declare TTY prerequisite"
- return 127
- fi
- perl "$TEST_DIRECTORY"/test-terminal.perl "$@"
- }
- fi
+ test "$(uname -s)" != Darwin &&
+
+ perl "$TEST_DIRECTORY"/test-terminal.perl \
+ sh -c "test -t 1 && test -t 2"
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 6ffd82fe32..e130c528fe 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -820,7 +820,18 @@ test_expect_success 'stdin -z update ref fails with bad old value' '
test_must_fail git rev-parse --verify -q $c
'
+test_expect_success 'stdin -z create ref fails when ref exists' '
+ git update-ref $c $m &&
+ git rev-parse "$c" >expect &&
+ printf $F "create $c" "$m~1" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
+ git rev-parse "$c" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'stdin -z create ref fails with bad new value' '
+ git update-ref -d "$c" &&
printf $F "create $c" "does-not-exist" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
grep "fatal: invalid new value for ref $c: does-not-exist" err &&
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 3fa171541a..58b792bf20 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -82,11 +82,9 @@ test_expect_success 'date parser recognizes time_t overflow' '
'
# date is within 2^63-1, but enough to choke glibc's gmtime
-test_expect_success 'absurdly far-in-future dates produce sentinel' '
+test_expect_success 'absurdly far-in-future date' '
commit=$(munge_author_date HEAD 999999999999999999) &&
- echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
- git log -1 --format=%ad $commit >actual &&
- test_cmp expect actual
+ git log -1 --format=%ad $commit
'
test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index ab28594c62..b212f83db7 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -113,6 +113,26 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
git rev-parse origin/master
'
+test_expect_success 'fetch --prune handles overlapping refspecs' '
+ cd "$D" &&
+ git update-ref refs/pull/42/head master &&
+ git clone . prune-overlapping &&
+ cd prune-overlapping &&
+ git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+
+ git fetch --prune origin &&
+ git rev-parse origin/master &&
+ git rev-parse origin/pr/42 &&
+
+ git config --unset-all remote.origin.fetch
+ git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+ git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
+
+ git fetch --prune origin &&
+ git rev-parse origin/master &&
+ git rev-parse origin/pr/42
+'
+
test_expect_success 'fetch --prune --tags prunes branches but not tags' '
cd "$D" &&
git clone . prune-tags &&
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index e3c8c2c1b8..215d43d6a6 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -294,7 +294,8 @@ test_expect_success 'setup submodule' '
git submodule add ./. sub &&
echo content >file &&
git add file &&
- git commit -m "added sub and file"
+ git commit -m "added sub and file" &&
+ git branch submodule
'
test_expect_success 'git mv cannot move a submodule in a file' '
@@ -463,4 +464,14 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
! test -s actual
'
+test_expect_success 'mv -k does not accidentally destroy submodules' '
+ git checkout submodule &&
+ mkdir dummy dest &&
+ git mv -k dummy sub dest &&
+ git status --porcelain >actual &&
+ grep "^R sub -> dest/sub" actual &&
+ git reset --hard &&
+ git checkout .
+'
+
test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 7d467c034a..741ec08576 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -38,7 +38,7 @@ You have unmerged paths.
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- deleted by us: foo
+ deleted by us: foo
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -142,8 +142,8 @@ You have unmerged paths.
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- both added: conflict.txt
- deleted by them: main.txt
+ both added: conflict.txt
+ deleted by them: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -175,9 +175,9 @@ You have unmerged paths.
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
- both deleted: main.txt
- added by them: sub_master.txt
- added by us: sub_second.txt
+ both deleted: main.txt
+ added by them: sub_master.txt
+ added by us: sub_second.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -203,7 +203,7 @@ Changes to be committed:
Unmerged paths:
(use "git rm <file>..." to mark resolution)
- both deleted: main.txt
+ both deleted: main.txt
Untracked files not listed (use -u option to show untracked files)
EOF
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 357375151d..03dce09cfe 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -134,14 +134,26 @@ test_expect_success 'with hook (-c)' '
test_expect_success 'with hook (merge)' '
- head=`git rev-parse HEAD` &&
- git checkout -b other HEAD@{1} &&
- echo "more" >> file &&
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
+ git add file &&
+ git commit -m other &&
+ git checkout - &&
+ git merge --no-ff other &&
+ test "`git log -1 --pretty=format:%s`" = "merge (no editor)"
+'
+
+test_expect_success 'with hook and editor (merge)' '
+
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
git add file &&
git commit -m other &&
git checkout - &&
- git merge other &&
- test "`git log -1 --pretty=format:%s`" = merge
+ env GIT_EDITOR="\"\$FAKE_EDITOR\"" git merge --no-ff -e other &&
+ test "`git log -1 --pretty=format:%s`" = "merge"
'
cat > "$HOOK" <<'EOF'
@@ -151,34 +163,37 @@ EOF
test_expect_success 'with failing hook' '
+ test_when_finished "git checkout -f master" &&
head=`git rev-parse HEAD` &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
'
test_expect_success 'with failing hook (--no-verify)' '
+ test_when_finished "git checkout -f master" &&
head=`git rev-parse HEAD` &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
'
test_expect_success 'with failing hook (merge)' '
+ test_when_finished "git checkout -f master" &&
git checkout -B other HEAD@{1} &&
echo "more" >> file &&
git add file &&
rm -f "$HOOK" &&
git commit -m other &&
- write_script "$HOOK" <<-EOF
+ write_script "$HOOK" <<-EOF &&
exit 1
EOF
git checkout - &&
- test_must_fail git merge other
+ test_must_fail git merge --no-ff other
'
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 3cec57af1e..68ad2d7454 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -33,7 +33,7 @@ You have unmerged paths.
Unmerged paths:
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -87,7 +87,7 @@ Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -146,7 +146,7 @@ Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -602,7 +602,7 @@ rebase in progress; onto $ONTO
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
Unmerged paths:
- both modified: main.txt
+ both modified: main.txt
no changes added to commit
EOF
@@ -636,7 +636,7 @@ You are currently cherry-picking commit $TO_CHERRY_PICK.
Unmerged paths:
(use "git add <file>..." to mark resolution)
- both modified: main.txt
+ both modified: main.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
@@ -707,7 +707,7 @@ Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
- both modified: to-revert.txt
+ both modified: to-revert.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
diff --git a/t/t7514-commit-patch.sh b/t/t7514-commit-patch.sh
new file mode 100755
index 0000000000..998a2103c7
--- /dev/null
+++ b/t/t7514-commit-patch.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='hunk edit with "commit -p -m"'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL
+then
+ skip_all="skipping '$test_description' tests, perl not available"
+ test_done
+fi
+
+test_expect_success 'setup (initial)' '
+ echo line1 >file &&
+ git add file &&
+ git commit -m commit1
+'
+
+test_expect_success 'edit hunk "commit -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit2 file &&
+ test -r editor_was_started
+'
+
+test_expect_success 'edit hunk "commit --dry-run -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit3 file &&
+ test -r editor_was_started
+'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1531c241c0..3c7cb1d774 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -277,7 +277,7 @@ error "Test script did not set test_description."
if test "$help" = "t"
then
- echo "$test_description"
+ printf '%s\n' "$test_description"
exit 0
fi
@@ -328,7 +328,7 @@ test_failure_ () {
test_failure=$(($test_failure + 1))
say_color error "not ok $test_count - $1"
shift
- echo "$@" | sed -e 's/^/# /'
+ printf '%s\n' "$*" | sed -e 's/^/# /'
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
}
diff --git a/upload-pack.c b/upload-pack.c
index 0c44f6b292..a3c52f691d 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -81,7 +81,7 @@ static void create_pack_file(void)
const char *argv[12];
int i, arg = 0;
FILE *pipe_fd;
- char *shallow_file = NULL;
+ const char *shallow_file = NULL;
if (shallow_nr) {
shallow_file = setup_temporary_shallow(NULL);
@@ -242,11 +242,6 @@ static void create_pack_file(void)
error("git upload-pack: git-pack-objects died with error.");
goto fail;
}
- if (shallow_file) {
- if (*shallow_file)
- unlink(shallow_file);
- free(shallow_file);
- }
/* flush the data */
if (0 <= buffered) {
diff --git a/wt-status.c b/wt-status.c
index a452407dad..9bf96c3af0 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -245,53 +245,92 @@ static void wt_status_print_trailer(struct wt_status *s)
#define quote_path quote_path_relative
-static void wt_status_print_unmerged_data(struct wt_status *s,
- struct string_list_item *it)
+static const char *wt_status_unmerged_status_string(int stagemask)
{
- const char *c = color(WT_STATUS_UNMERGED, s);
- struct wt_status_change_data *d = it->util;
- struct strbuf onebuf = STRBUF_INIT;
- const char *one, *how = _("bug");
-
- one = quote_path(it->string, s->prefix, &onebuf);
- status_printf(s, color(WT_STATUS_HEADER, s), "\t");
- switch (d->stagemask) {
- case 1: how = _("both deleted:"); break;
- case 2: how = _("added by us:"); break;
- case 3: how = _("deleted by them:"); break;
- case 4: how = _("added by them:"); break;
- case 5: how = _("deleted by us:"); break;
- case 6: how = _("both added:"); break;
- case 7: how = _("both modified:"); break;
+ switch (stagemask) {
+ case 1:
+ return _("both deleted:");
+ case 2:
+ return _("added by us:");
+ case 3:
+ return _("deleted by them:");
+ case 4:
+ return _("added by them:");
+ case 5:
+ return _("deleted by us:");
+ case 6:
+ return _("both added:");
+ case 7:
+ return _("both modified:");
+ default:
+ die(_("bug: unhandled unmerged status %x"), stagemask);
}
- status_printf_more(s, c, "%-20s%s\n", how, one);
- strbuf_release(&onebuf);
}
static const char *wt_status_diff_status_string(int status)
{
switch (status) {
case DIFF_STATUS_ADDED:
- return _("new file");
+ return _("new file:");
case DIFF_STATUS_COPIED:
- return _("copied");
+ return _("copied:");
case DIFF_STATUS_DELETED:
- return _("deleted");
+ return _("deleted:");
case DIFF_STATUS_MODIFIED:
- return _("modified");
+ return _("modified:");
case DIFF_STATUS_RENAMED:
- return _("renamed");
+ return _("renamed:");
case DIFF_STATUS_TYPE_CHANGED:
- return _("typechange");
+ return _("typechange:");
case DIFF_STATUS_UNKNOWN:
- return _("unknown");
+ return _("unknown:");
case DIFF_STATUS_UNMERGED:
- return _("unmerged");
+ return _("unmerged:");
default:
return NULL;
}
}
+static int maxwidth(const char *(*label)(int), int minval, int maxval)
+{
+ int result = 0, i;
+
+ for (i = minval; i <= maxval; i++) {
+ const char *s = label(i);
+ int len = s ? utf8_strwidth(s) : 0;
+ if (len > result)
+ result = len;
+ }
+ return result;
+}
+
+static void wt_status_print_unmerged_data(struct wt_status *s,
+ struct string_list_item *it)
+{
+ const char *c = color(WT_STATUS_UNMERGED, s);
+ struct wt_status_change_data *d = it->util;
+ struct strbuf onebuf = STRBUF_INIT;
+ static char *padding;
+ static int label_width;
+ const char *one, *how;
+ int len;
+
+ if (!padding) {
+ label_width = maxwidth(wt_status_unmerged_status_string, 1, 7);
+ label_width += strlen(" ");
+ padding = xmallocz(label_width);
+ memset(padding, ' ', label_width);
+ }
+
+ one = quote_path(it->string, s->prefix, &onebuf);
+ status_printf(s, color(WT_STATUS_HEADER, s), "\t");
+
+ how = wt_status_unmerged_status_string(d->stagemask);
+ len = label_width - utf8_strwidth(how);
+ status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one);
+ strbuf_release(&onebuf);
+}
+
static void wt_status_print_change_data(struct wt_status *s,
int change_type,
struct string_list_item *it)
@@ -305,21 +344,16 @@ static void wt_status_print_change_data(struct wt_status *s,
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
struct strbuf extra = STRBUF_INIT;
static char *padding;
+ static int label_width;
const char *what;
int len;
if (!padding) {
- int width = 0;
- /* If DIFF_STATUS_* uses outside this range, we're in trouble */
- for (status = 'A'; status <= 'Z'; status++) {
- what = wt_status_diff_status_string(status);
- len = what ? strlen(what) : 0;
- if (len > width)
- width = len;
- }
- width += 2; /* colon and a space */
- padding = xmallocz(width);
- memset(padding, ' ', width);
+ /* If DIFF_STATUS_* uses outside the range [A..Z], we're in trouble */
+ label_width = maxwidth(wt_status_diff_status_string, 'A', 'Z');
+ label_width += strlen(" ");
+ padding = xmallocz(label_width);
+ memset(padding, ' ', label_width);
}
one_name = two_name = it->string;
@@ -355,14 +389,13 @@ static void wt_status_print_change_data(struct wt_status *s,
what = wt_status_diff_status_string(status);
if (!what)
die(_("bug: unhandled diff status %c"), status);
- /* 1 for colon, which is not part of "what" */
- len = strlen(padding) - (utf8_strwidth(what) + 1);
+ len = label_width - utf8_strwidth(what);
assert(len >= 0);
if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)
- status_printf_more(s, c, "%s:%.*s%s -> %s",
+ status_printf_more(s, c, "%s%.*s%s -> %s",
what, len, padding, one, two);
else
- status_printf_more(s, c, "%s:%.*s%s",
+ status_printf_more(s, c, "%s%.*s%s",
what, len, padding, one);
if (extra.len) {
status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
@@ -1509,19 +1542,21 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
return;
}
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
color_fprintf(s->fp, header_color, " [");
if (upstream_is_gone) {
- color_fprintf(s->fp, header_color, _("gone"));
+ color_fprintf(s->fp, header_color, LABEL(N_("gone")));
} else if (!num_ours) {
- color_fprintf(s->fp, header_color, _("behind "));
+ color_fprintf(s->fp, header_color, LABEL(N_("behind ")));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
} else if (!num_theirs) {
- color_fprintf(s->fp, header_color, _("ahead "));
+ color_fprintf(s->fp, header_color, LABEL(N_(("ahead "))));
color_fprintf(s->fp, branch_color_local, "%d", num_ours);
} else {
- color_fprintf(s->fp, header_color, _("ahead "));
+ color_fprintf(s->fp, header_color, LABEL(N_(("ahead "))));
color_fprintf(s->fp, branch_color_local, "%d", num_ours);
- color_fprintf(s->fp, header_color, _(", behind "));
+ color_fprintf(s->fp, header_color, ", %s", LABEL(N_("behind ")));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
}
@@ -1566,5 +1601,6 @@ void wt_porcelain_print(struct wt_status *s)
s->use_color = 0;
s->relative_paths = 0;
s->prefix = NULL;
+ s->no_gettext = 1;
wt_shortstatus_print(s);
}
diff --git a/wt-status.h b/wt-status.h
index 30a4812583..82f6ce64f8 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -50,6 +50,7 @@ struct wt_status {
enum commit_whence whence;
int nowarn;
int use_color;
+ int no_gettext;
int display_comment_prefix;
int relative_paths;
int submodule_summary;