2 changes: 1 addition & 1 deletion Documentation/technical/reftable.txt
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ especially if readers will not use the object name to ref mapping.
Object blocks use unique, abbreviated 2-32 object name keys, mapping to
ref blocks containing references pointing to that object directly, or as
the peeled value of an annotated tag. Like ref blocks, object blocks use
the file's standard block size. The abbrevation length is available in
the file's standard block size. The abbreviation length is available in
the footer as `obj_id_len`.

To save space in small files, object blocks may be omitted if the ref
Expand Down
2 changes: 1 addition & 1 deletion GIT-VERSION-GEN
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

GVF=GIT-VERSION-FILE
DEF_VER=v2.30.0
DEF_VER=v2.30.1

LF='
'
Expand Down
31 changes: 14 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -777,20 +777,6 @@ BUILT_INS += git-status$X
BUILT_INS += git-switch$X
BUILT_INS += git-whatchanged$X

# what 'all' will build and 'install' will install in gitexecdir,
# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS)
ifeq (,$(SKIP_DASHED_BUILT_INS))
ALL_COMMANDS_TO_INSTALL += $(BUILT_INS)
else
# git-upload-pack, git-receive-pack and git-upload-archive are special: they
# are _expected_ to be present in the `bin/` directory in their dashed form.
ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)
ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
endif

# what 'all' will build but not install in gitexecdir
OTHER_PROGRAMS = git$X

Expand Down Expand Up @@ -1226,6 +1212,20 @@ ifdef DEVELOPER
include config.mak.dev
endif

# what 'all' will build and 'install' will install in gitexecdir,
# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS)
ifeq (,$(SKIP_DASHED_BUILT_INS))
ALL_COMMANDS_TO_INSTALL += $(BUILT_INS)
else
# git-upload-pack, git-receive-pack and git-upload-archive are special: they
# are _expected_ to be present in the `bin/` directory in their dashed form.
ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)
ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
endif

ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
ALL_LDFLAGS = $(LDFLAGS)

Expand Down Expand Up @@ -1554,9 +1554,6 @@ endif
ifdef NO_SYMLINK_HEAD
BASIC_CFLAGS += -DNO_SYMLINK_HEAD
endif
ifdef GETTEXT_POISON
$(warning The GETTEXT_POISON option has been removed in favor of runtime GIT_TEST_GETTEXT_POISON. See t/README!)
endif
ifdef NO_GETTEXT
BASIC_CFLAGS += -DNO_GETTEXT
USE_GETTEXT_SCHEME ?= fallthrough
Expand Down
2 changes: 1 addition & 1 deletion RelNotes
4 changes: 2 additions & 2 deletions builtin/am.c
Original file line number Diff line number Diff line change
Expand Up @@ -2284,10 +2284,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("skip the current patch"),
RESUME_SKIP),
OPT_CMDMODE(0, "abort", &resume.mode,
N_("restore the original branch and abort the patching operation."),
N_("restore the original branch and abort the patching operation"),
RESUME_ABORT),
OPT_CMDMODE(0, "quit", &resume.mode,
N_("abort the patching operation but keep HEAD where it is."),
N_("abort the patching operation but keep HEAD where it is"),
RESUME_QUIT),
{ OPTION_CALLBACK, 0, "show-current-patch", &resume.mode,
"(diff|raw)",
Expand Down
48 changes: 24 additions & 24 deletions builtin/blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -866,33 +866,33 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
const char *revs_file = NULL;
const char *contents_from = NULL;
const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
OPT_BOOL('b', NULL, &blank_boundary, N_("Do not show object names of boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN),
OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT),
OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP),
OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME),
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("Ignore <rev> when blaming")),
OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
OPT_BOOL(0, "incremental", &incremental, N_("show blame entries as we find them, incrementally")),
OPT_BOOL('b', NULL, &blank_boundary, N_("do not show object names of boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("show work cost statistics")),
OPT_BOOL(0, "progress", &show_progress, N_("force progress reporting")),
OPT_BIT(0, "score-debug", &output_option, N_("show output score for blame entries"), OUTPUT_SHOW_SCORE),
OPT_BIT('f', "show-name", &output_option, N_("show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, N_("show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
OPT_BIT('p', "porcelain", &output_option, N_("show in a format designed for machine consumption"), OUTPUT_PORCELAIN),
OPT_BIT(0, "line-porcelain", &output_option, N_("show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
OPT_BIT('c', NULL, &output_option, N_("use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT),
OPT_BIT('t', NULL, &output_option, N_("show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP),
OPT_BIT('l', NULL, &output_option, N_("show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME),
OPT_BIT('s', NULL, &output_option, N_("suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("ignore <rev> when blaming")),
OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("ignore revisions from <file>")),
OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),
OPT_CALLBACK_F('M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback),
OPT_BIT(0, "minimal", &xdl_opts, N_("spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("use <file>'s contents as the final image")),
OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),
OPT_CALLBACK_F('M', NULL, &opt, N_("score"), N_("find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback),
OPT_STRING_LIST('L', NULL, &range_list, N_("range"),
N_("Process only line range <start>,<end> or function :<funcname>")),
N_("process only line range <start>,<end> or function :<funcname>")),
OPT__ABBREV(&abbrev),
OPT_END()
};
Expand Down
6 changes: 4 additions & 2 deletions builtin/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
print_current_branch_name();
return 0;
} else if (list) {
/* git branch --local also shows HEAD when it is detached */
/* git branch --list also shows HEAD when it is detached */
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
filter.kind |= FILTER_REFS_DETACHED_HEAD;
filter.name_patterns = argv;
Expand All @@ -739,7 +739,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
*/
if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
ref_sorting_set_sort_flags_all(
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
Expand Down
6 changes: 3 additions & 3 deletions builtin/commit-graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static int graph_verify(int argc, const char **argv)
static struct option builtin_commit_graph_verify_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("The object directory to store the graph")),
N_("the object directory to store the graph")),
OPT_BOOL(0, "shallow", &opts.shallow,
N_("if the commit-graph is split, only verify the tip file")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
Expand Down Expand Up @@ -208,7 +208,7 @@ static int graph_write(int argc, const char **argv)
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("The object directory to store the graph")),
N_("the object directory to store the graph")),
OPT_BOOL(0, "reachable", &opts.reachable,
N_("start walk at all refs")),
OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
Expand Down Expand Up @@ -314,7 +314,7 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
static struct option builtin_commit_graph_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("The object directory to store the graph")),
N_("the object directory to store the graph")),
OPT_END(),
};

Expand Down
22 changes: 11 additions & 11 deletions builtin/fast-export.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,32 +1205,32 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
N_("select handling of commit messages in an alternate encoding"),
parse_opt_reencode_mode),
OPT_STRING(0, "export-marks", &export_filename, N_("file"),
N_("Dump marks to this file")),
N_("dump marks to this file")),
OPT_STRING(0, "import-marks", &import_filename, N_("file"),
N_("Import marks from this file")),
N_("import marks from this file")),
OPT_STRING(0, "import-marks-if-exists",
&import_filename_if_exists,
N_("file"),
N_("Import marks from this file if it exists")),
N_("import marks from this file if it exists")),
OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
N_("Fake a tagger when tags lack one")),
N_("fake a tagger when tags lack one")),
OPT_BOOL(0, "full-tree", &full_tree,
N_("Output full tree for each commit")),
N_("output full tree for each commit")),
OPT_BOOL(0, "use-done-feature", &use_done_feature,
N_("Use the done feature to terminate the stream")),
OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
N_("use the done feature to terminate the stream")),
OPT_BOOL(0, "no-data", &no_data, N_("skip output of blob data")),
OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
N_("Apply refspec to exported refs")),
N_("apply refspec to exported refs")),
OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
OPT_CALLBACK_F(0, "anonymize-map", &anonymized_seeds, N_("from:to"),
N_("convert <from> to <to> in anonymized output"),
PARSE_OPT_NONEG, parse_opt_anonymize_map),
OPT_BOOL(0, "reference-excluded-parents",
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
&reference_excluded_commits, N_("reference parents which are not in fast-export stream by object id")),
OPT_BOOL(0, "show-original-ids", &show_original_ids,
N_("Show original object ids of blobs/commits")),
N_("show original object ids of blobs/commits")),
OPT_BOOL(0, "mark-tags", &mark_tags,
N_("Label tags with mark ids")),
N_("label tags with mark ids")),

OPT_END()
};
Expand Down
2 changes: 1 addition & 1 deletion builtin/for-each-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)

if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;

filter.name_patterns = argv;
Expand Down
7 changes: 7 additions & 0 deletions builtin/for-each-repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
values = repo_config_get_value_multi(the_repository,
config_key);

/*
* Do nothing on an empty list, which is equivalent to the case
* where the config variable does not exist at all.
*/
if (!values)
return 0;

for (i = 0; !result && i < values->nr; i++)
result = run_command_on_repo(values->items[i].string, &args);

Expand Down
6 changes: 3 additions & 3 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ static void process_log_file(void)
*/
int saved_errno = errno;
fprintf(stderr, _("Failed to fstat %s: %s"),
get_tempfile_path(log_lock.tempfile),
get_lock_file_path(&log_lock),
strerror(saved_errno));
fflush(stderr);
commit_lock_file(&log_lock);
Expand Down Expand Up @@ -1518,7 +1518,7 @@ static int update_background_schedule(int run_maintenance)
strvec_split(&crontab_list.args, crontab_name);
strvec_push(&crontab_list.args, "-l");
crontab_list.in = -1;
crontab_list.out = dup(lk.tempfile->fd);
crontab_list.out = dup(get_lock_file_fd(&lk));
crontab_list.git_cmd = 0;

if (start_command(&crontab_list)) {
Expand All @@ -1533,7 +1533,7 @@ static int update_background_schedule(int run_maintenance)
* Read from the .lock file, filtering out the old
* schedule while appending the new schedule.
*/
cron_list = fdopen(lk.tempfile->fd, "r");
cron_list = fdopen(get_lock_file_fd(&lk), "r");
rewind(cron_list);

strvec_split(&crontab_edit.args, crontab_name);
Expand Down
8 changes: 4 additions & 4 deletions builtin/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
const struct option builtin_log_options[] = {
OPT__QUIET(&quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("use mail map file")),
OPT_ALIAS(0, "mailmap", "use-mailmap"),
OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
N_("pattern"), N_("only decorate refs that match <pattern>")),
Expand All @@ -186,7 +186,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback),
OPT_CALLBACK('L', NULL, &line_cb, "range:file",
N_("Trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
N_("trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
log_line_range_callback),
OPT_END()
};
Expand Down Expand Up @@ -1757,13 +1757,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
N_("max length of output filename")),
OPT_CALLBACK_F(0, "rfc", &rev, NULL,
N_("Use [RFC PATCH] instead of [PATCH]"),
N_("use [RFC PATCH] instead of [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
N_("cover-from-description-mode"),
N_("generate parts of a cover letter based on a branch's description")),
OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"),
N_("Use [<prefix>] instead of [PATCH]"),
N_("use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback),
OPT_CALLBACK_F('o', "output-directory", &output_directory,
N_("dir"), N_("store resulting files in <dir>"),
Expand Down
4 changes: 3 additions & 1 deletion builtin/rebase.c
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
} else if (!get_oid(branch_name, &options.orig_head))
} else if (!get_oid(branch_name, &options.orig_head) &&
lookup_commit_reference(the_repository,
&options.orig_head))
options.head_name = NULL;
else
die(_("fatal: no such branch/commit '%s'"),
Expand Down
10 changes: 5 additions & 5 deletions builtin/shortlog.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,19 +360,19 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)

const struct option options[] = {
OPT_BIT('c', "committer", &log.groups,
N_("Group by committer rather than author"),
N_("group by committer rather than author"),
SHORTLOG_GROUP_COMMITTER),
OPT_BOOL('n', "numbered", &log.sort_by_number,
N_("sort output according to the number of commits per author")),
OPT_BOOL('s', "summary", &log.summary,
N_("Suppress commit descriptions, only provides commit count")),
N_("suppress commit descriptions, only provides commit count")),
OPT_BOOL('e', "email", &log.email,
N_("Show the email address of each author")),
N_("show the email address of each author")),
OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"),
N_("Linewrap output"), PARSE_OPT_OPTARG,
N_("linewrap output"), PARSE_OPT_OPTARG,
&parse_wrap_args),
OPT_CALLBACK(0, "group", &log, N_("field"),
N_("Group by field"), parse_group_option),
N_("group by field"), parse_group_option),
OPT_END(),
};

Expand Down
165 changes: 116 additions & 49 deletions builtin/stash.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,35 +325,6 @@ static void add_diff_to_buf(struct diff_queue_struct *q,
}
}

static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
{
struct child_process cp = CHILD_PROCESS_INIT;
const char *c_tree_hex = oid_to_hex(c_tree);

/*
* diff-index is very similar to diff-tree above, and should be
* converted together with update_index.
*/
cp.git_cmd = 1;
strvec_pushl(&cp.args, "diff-index", "--cached", "--name-only",
"--diff-filter=A", NULL);
strvec_push(&cp.args, c_tree_hex);
return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
}

static int update_index(struct strbuf *out)
{
struct child_process cp = CHILD_PROCESS_INIT;

/*
* Update-index is very complicated and may need to have a public
* function exposed in order to remove this forking.
*/
cp.git_cmd = 1;
strvec_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
}

static int restore_untracked(struct object_id *u_tree)
{
int res;
Expand Down Expand Up @@ -385,6 +356,121 @@ static int restore_untracked(struct object_id *u_tree)
return res;
}

static void unstage_changes_unless_new(struct object_id *orig_tree)
{
/*
* When we enter this function, there has been a clean merge of
* relevant trees, and the merge logic always stages whatever merges
* cleanly. We want to unstage those changes, unless it corresponds
* to a file that didn't exist as of orig_tree.
*
* However, if any SKIP_WORKTREE path is modified relative to
* orig_tree, then we want to clear the SKIP_WORKTREE bit and write
* it to the worktree before unstaging.
*/

struct checkout state = CHECKOUT_INIT;
struct diff_options diff_opts;
struct lock_file lock = LOCK_INIT;
int i;

/* If any entries have skip_worktree set, we'll have to check 'em out */
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
state.istate = &the_index;

/*
* Step 1: get a difference between orig_tree (which corresponding
* to the index before a merge was run) and the current index
* (reflecting the changes brought in by the merge).
*/
diff_setup(&diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.detect_rename = 0;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&diff_opts);

do_diff_cache(orig_tree, &diff_opts);
diffcore_std(&diff_opts);

/* Iterate over the paths that changed due to the merge... */
for (i = 0; i < diff_queued_diff.nr; i++) {
struct diff_filepair *p;
struct cache_entry *ce;
int pos;

/* Look up the path's position in the current index. */
p = diff_queued_diff.queue[i];
pos = index_name_pos(&the_index, p->two->path,
strlen(p->two->path));

/*
* Step 2: Place changes in the working tree
*
* Stash is about restoring changes *to the working tree*.
* So if the merge successfully got a new version of some
* path, but left it out of the working tree, then clear the
* SKIP_WORKTREE bit and write it to the working tree.
*/
if (pos >= 0 && ce_skip_worktree(active_cache[pos])) {
struct stat st;

ce = active_cache[pos];
if (!lstat(ce->name, &st)) {
/* Conflicting path present; relocate it */
struct strbuf new_path = STRBUF_INIT;
int fd;

strbuf_addf(&new_path,
"%s.stash.XXXXXX", ce->name);
fd = xmkstemp(new_path.buf);
close(fd);
printf(_("WARNING: Untracked file in way of "
"tracked file! Renaming\n "
" %s -> %s\n"
" to make room.\n"),
ce->name, new_path.buf);
if (rename(ce->name, new_path.buf))
die("Failed to move %s to %s\n",
ce->name, new_path.buf);
strbuf_release(&new_path);
}
checkout_entry(ce, &state, NULL, NULL);
ce->ce_flags &= ~CE_SKIP_WORKTREE;
}

/*
* Step 3: "unstage" changes, as long as they are still tracked
*/
if (p->one->oid_valid) {
/*
* Path existed in orig_tree; restore index entry
* from that tree in order to "unstage" the changes.
*/
int option = ADD_CACHE_OK_TO_REPLACE;
if (pos < 0)
option = ADD_CACHE_OK_TO_ADD;

ce = make_cache_entry(&the_index,
p->one->mode,
&p->one->oid,
p->one->path,
0, 0);
add_index_entry(&the_index, ce, option);
}
}
diff_flush(&diff_opts);

/*
* Step 4: write the new index to disk
*/
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write index."));
}

static int do_apply_stash(const char *prefix, struct stash_info *info,
int index, int quiet)
{
Expand Down Expand Up @@ -467,26 +553,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
if (reset_tree(&index_tree, 0, 0))
return -1;
} else {
struct strbuf out = STRBUF_INIT;

if (get_newly_staged(&out, &c_tree)) {
strbuf_release(&out);
return -1;
}

if (reset_tree(&c_tree, 0, 1)) {
strbuf_release(&out);
return -1;
}

ret = update_index(&out);
strbuf_release(&out);
if (ret)
return -1;

/* read back the result of update_index() back from the disk */
discard_cache();
read_cache();
unstage_changes_unless_new(&c_tree);
}

if (!quiet) {
Expand Down
24 changes: 12 additions & 12 deletions builtin/submodule--helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,9 +562,9 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
struct module_list list = MODULE_LIST_INIT;

struct option module_foreach_options[] = {
OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
OPT_BOOL(0, "recursive", &info.recursive,
N_("Recurse into nested submodules")),
N_("recurse into nested submodules")),
OPT_END()
};

Expand Down Expand Up @@ -706,7 +706,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
int quiet = 0;

struct option module_init_options[] = {
OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
OPT__QUIET(&quiet, N_("suppress output for initializing a submodule")),
OPT_END()
};

Expand Down Expand Up @@ -883,8 +883,8 @@ static int module_status(int argc, const char **argv, const char *prefix)
int quiet = 0;

struct option module_status_options[] = {
OPT__QUIET(&quiet, N_("Suppress submodule status output")),
OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
OPT__QUIET(&quiet, N_("suppress submodule status output")),
OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
OPT_END()
};
Expand Down Expand Up @@ -1482,9 +1482,9 @@ static int module_sync(int argc, const char **argv, const char *prefix)
int recursive = 0;

struct option module_sync_options[] = {
OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
OPT_BOOL(0, "recursive", &recursive,
N_("Recurse into nested submodules")),
N_("recurse into nested submodules")),
OPT_END()
};

Expand Down Expand Up @@ -1620,9 +1620,9 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
int all = 0;

struct option module_deinit_options[] = {
OPT__QUIET(&quiet, N_("Suppress submodule status output")),
OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
OPT__QUIET(&quiet, N_("suppress submodule status output")),
OPT__FORCE(&force, N_("remove submodule working trees even if they contain local changes"), 0),
OPT_BOOL(0, "all", &all, N_("unregister all submodules")),
OPT_END()
};

Expand Down Expand Up @@ -2337,7 +2337,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "dissociate", &suc.dissociate,
N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &suc.depth, "<depth>",
N_("Create a shallow clone truncated to the "
N_("create a shallow clone truncated to the "
"specified number of revisions")),
OPT_INTEGER('j', "jobs", &suc.max_jobs,
N_("parallel jobs")),
Expand Down Expand Up @@ -2678,7 +2678,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
char *config_name;

struct option options[] = {
OPT__QUIET(&quiet, N_("Suppress output for setting url of a submodule")),
OPT__QUIET(&quiet, N_("suppress output for setting url of a submodule")),
OPT_END()
};
const char *const usage[] = {
Expand Down
2 changes: 1 addition & 1 deletion builtin/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
}
if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
int ret;
Expand Down
8 changes: 4 additions & 4 deletions ci/install-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ osx-clang|osx-gcc)
test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
brew cask install --no-quarantine perforce || {
brew install --cask --no-quarantine perforce || {
# Update the definitions and try again
cask_repo="$(brew --repository)"/Library/Taps/homebrew/homebrew-cask &&
git -C "$cask_repo" pull --no-stat &&
brew cask install --no-quarantine perforce
git -C "$cask_repo" pull --no-stat --ff-only &&
brew install --cask --no-quarantine perforce
} ||
brew install caskroom/cask/perforce
brew install homebrew/cask/perforce
case "$jobname" in
osx-gcc)
brew install gcc@9
Expand Down
6 changes: 3 additions & 3 deletions commit-graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -1694,8 +1694,8 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
} else {
hold_lock_file_for_update_mode(&lk, ctx->graph_name,
LOCK_DIE_ON_ERROR, 0444);
fd = lk.tempfile->fd;
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
fd = get_lock_file_fd(&lk);
f = hashfd(fd, get_lock_file_path(&lk));
}

chunks[0].id = GRAPH_CHUNKID_OIDFANOUT;
Expand Down Expand Up @@ -1833,7 +1833,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
result = rename(ctx->graph_name, final_graph_name);

for (i = 0; i < ctx->num_commit_graphs_after; i++)
fprintf(lk.tempfile->fp, "%s\n", ctx->commit_graph_hash_after[i]);
fprintf(get_lock_file_fp(&lk), "%s\n", ctx->commit_graph_hash_after[i]);

if (result) {
error(_("failed to rename temporary commit-graph file"));
Expand Down
2 changes: 2 additions & 0 deletions connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,8 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport,
target_host = xstrdup(hostandport);

transport_check_allowed("git");
if (strchr(target_host, '\n') || strchr(path, '\n'))
die(_("newline is forbidden in git:// hosts and repo paths"));

/*
* These underlying connection commands die() if they
Expand Down
2 changes: 1 addition & 1 deletion fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ static int check_submodule_url(const char *url)
if (looks_like_command_line_option(url))
return -1;

if (submodule_url_is_relative(url)) {
if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
char *decoded;
const char *next;
int has_nl;
Expand Down
94 changes: 15 additions & 79 deletions gettext.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,88 +87,24 @@ static int test_vsnprintf(const char *fmt, ...)

static void init_gettext_charset(const char *domain)
{
/*
This trick arranges for messages to be emitted in the user's
requested encoding, but avoids setting LC_CTYPE from the
environment for the whole program.
This primarily done to avoid a bug in vsnprintf in the GNU C
Library [1]. which triggered a "your vsnprintf is broken" error
on Git's own repository when inspecting v0.99.6~1 under a UTF-8
locale.
That commit contains a ISO-8859-1 encoded author name, which
the locale aware vsnprintf(3) won't interpolate in the format
argument, due to mismatch between the data encoding and the
locale.
Even if it wasn't for that bug we wouldn't want to use LC_CTYPE at
this point, because it'd require auditing all the code that uses C
functions whose semantics are modified by LC_CTYPE.
But only setting LC_MESSAGES as we do creates a problem, since
we declare the encoding of our PO files[2] the gettext
implementation will try to recode it to the user's locale, but
without LC_CTYPE it'll emit something like this on 'git init'
under the Icelandic locale:
Bj? til t?ma Git lind ? /hlagh/.git/
Gettext knows about the encoding of our PO file, but we haven't
told it about the user's encoding, so all the non-US-ASCII
characters get encoded to question marks.
But we're in luck! We can set LC_CTYPE from the environment
only while we call nl_langinfo and
bind_textdomain_codeset. That suffices to tell gettext what
encoding it should emit in, so it'll now say:
Bjó til tóma Git lind í /hlagh/.git/
And the equivalent ISO-8859-1 string will be emitted under a
ISO-8859-1 locale.
With this change way we get the advantages of setting LC_CTYPE
(talk to the user in his language/encoding), without the major
drawbacks (changed semantics for C functions we rely on).
However foreign functions using other message catalogs that
aren't using our neat trick will still have a problem, e.g. if
we have to call perror(3):
#include <stdio.h>
#include <locale.h>
#include <errno.h>
int main(void)
{
setlocale(LC_MESSAGES, "");
setlocale(LC_CTYPE, "C");
errno = ENODEV;
perror("test");
return 0;
}
Running that will give you a message with question marks:
$ LANGUAGE= LANG=de_DE.utf8 ./test
test: Kein passendes Ger?t gefunden
The vsnprintf bug has been fixed since glibc 2.17.
Then we could simply set LC_CTYPE from the environment, which would
make things like the external perror(3) messages work.
See t/t0203-gettext-setlocale-sanity.sh's "gettext.c" tests for
regression tests.
1. http://sourceware.org/bugzilla/show_bug.cgi?id=6530
2. E.g. "Content-Type: text/plain; charset=UTF-8\n" in po/is.po
*/
setlocale(LC_CTYPE, "");
charset = locale_charset();
bind_textdomain_codeset(domain, charset);
/* the string is taken from v0.99.6~1 */

/*
* Work around an old bug fixed in glibc 2.17 (released on
* 2012-12-24), at the cost of potentially making translated
* messages from external functions like perror() emitted in
* the wrong encoding.
*
* The bug affected e.g. git.git's own 7eb93c89651 ([PATCH]
* Simplify git script, 2005-09-07), which is the origin of
* the "David_K\345gedal" test string.
*
* See a much longer comment added to this file in 5e9637c6297
* (i18n: add infrastructure for translating Git with gettext,
* 2011-11-18) for more details.
*/
if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0)
setlocale(LC_CTYPE, "C");
}
Expand Down
6 changes: 4 additions & 2 deletions git-mergetool--lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ show_tool_names () {
while read scriptname
do
setup_tool "$scriptname" 2>/dev/null
variants="$variants$(list_tool_variants)\n"
# We need an actual line feed here
variants="$variants
$(list_tool_variants)"
done
variants="$(echo "$variants" | sort | uniq)"
variants="$(echo "$variants" | sort -u)"

for toolname in $variants
do
Expand Down
2 changes: 1 addition & 1 deletion git-p4.py
Original file line number Diff line number Diff line change
Expand Up @@ -3031,7 +3031,7 @@ def streamOneP4File(self, file, contents):
regexp = re.compile(pattern, re.VERBOSE)
text = ''.join(decode_text_stream(c) for c in contents)
text = regexp.sub(r'$\1$', text)
contents = [ text ]
contents = [ encode_text_stream(text) ]

if self.largeFileSystem:
(git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
Expand Down
2 changes: 1 addition & 1 deletion midx.c
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);

hold_lock_file_for_update(&lk, midx_name, LOCK_DIE_ON_ERROR);
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
FREE_AND_NULL(midx_name);

if (packs.m)
Expand Down
6 changes: 3 additions & 3 deletions parse-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ typedef enum parse_opt_result parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
* stores pointers to the values to be filled.
*
* `argh`::
* token to explain the kind of argument this option wants. Keep it
* homogeneous across the repository. Should be wrapped by N_() for
* translation.
* token to explain the kind of argument this option wants. Does not
* begin in capital letter, and does not end with a full stop.
* Should be wrapped by N_() for translation.
*
* `help`::
* the short help associated to what the option does.
Expand Down
14 changes: 13 additions & 1 deletion patch-ids.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ static int init_patch_id_entry(struct patch_id *patch,
return 0;
}

struct patch_id *has_commit_patch_id(struct commit *commit,
struct patch_id *patch_id_iter_first(struct commit *commit,
struct patch_ids *ids)
{
struct patch_id patch;
Expand All @@ -104,6 +104,18 @@ struct patch_id *has_commit_patch_id(struct commit *commit,
return hashmap_get_entry(&ids->patches, &patch, ent, NULL);
}

struct patch_id *patch_id_iter_next(struct patch_id *cur,
struct patch_ids *ids)
{
return hashmap_get_next_entry(&ids->patches, cur, ent);
}

int has_commit_patch_id(struct commit *commit,
struct patch_ids *ids)
{
return !!patch_id_iter_first(commit, ids);
}

struct patch_id *add_commit_patch_id(struct commit *commit,
struct patch_ids *ids)
{
Expand Down
20 changes: 19 additions & 1 deletion patch-ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,25 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,
struct object_id *oid, int, int);
int init_patch_ids(struct repository *, struct patch_ids *);
int free_patch_ids(struct patch_ids *);

/* Add a patch_id for a single commit to the set. */
struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);

/* Returns true if the patch-id of "commit" is present in the set. */
int has_commit_patch_id(struct commit *commit, struct patch_ids *);

/*
* Iterate over all commits in the set whose patch id matches that of
* "commit", like:
*
* struct patch_id *cur;
* for (cur = patch_id_iter_first(commit, ids);
* cur;
* cur = patch_id_iter_next(cur, ids) {
* ... look at cur->commit
* }
*/
struct patch_id *patch_id_iter_first(struct commit *commit, struct patch_ids *);
struct patch_id *patch_id_iter_next(struct patch_id *cur, struct patch_ids *);

#endif /* PATCH_IDS_H */
12 changes: 6 additions & 6 deletions read-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -3014,10 +3014,10 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (ce_flush(&c, newfd, istate->oid.hash))
return -1;
if (close_tempfile_gently(tempfile)) {
error(_("could not close '%s'"), tempfile->filename.buf);
error(_("could not close '%s'"), get_tempfile_path(tempfile));
return -1;
}
if (stat(tempfile->filename.buf, &st))
if (stat(get_tempfile_path(tempfile), &st))
return -1;
istate->timestamp.sec = (unsigned int)st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
Expand Down Expand Up @@ -3058,10 +3058,10 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
* that is associated with the given "istate".
*/
trace2_region_enter_printf("index", "do_write_index", the_repository,
"%s", lock->tempfile->filename.buf);
"%s", get_lock_file_path(lock));
ret = do_write_index(istate, lock->tempfile, 0);
trace2_region_leave_printf("index", "do_write_index", the_repository,
"%s", lock->tempfile->filename.buf);
"%s", get_lock_file_path(lock));

if (ret)
return ret;
Expand Down Expand Up @@ -3158,10 +3158,10 @@ static int write_shared_index(struct index_state *istate,
move_cache_to_base_index(istate);

trace2_region_enter_printf("index", "shared/do_write_index",
the_repository, "%s", (*temp)->filename.buf);
the_repository, "%s", get_tempfile_path(*temp));
ret = do_write_index(si->base, *temp, 1);
trace2_region_leave_printf("index", "shared/do_write_index",
the_repository, "%s", (*temp)->filename.buf);
the_repository, "%s", get_tempfile_path(*temp));

if (ret)
return ret;
Expand Down
75 changes: 45 additions & 30 deletions ref-filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1536,36 +1536,27 @@ char *get_head_description(void)
struct wt_status_state state;
memset(&state, 0, sizeof(state));
wt_status_get_state(the_repository, &state, 1);

/*
* The ( character must be hard-coded and not part of a localizable
* string, since the description is used as a sort key and compared
* with ref names.
*/
strbuf_addch(&desc, '(');
if (state.rebase_in_progress ||
state.rebase_interactive_in_progress) {
if (state.branch)
strbuf_addf(&desc, _("no branch, rebasing %s"),
strbuf_addf(&desc, _("(no branch, rebasing %s)"),
state.branch);
else
strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
state.detached_from);
} else if (state.bisect_in_progress)
strbuf_addf(&desc, _("no branch, bisect started on %s"),
strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
state.branch);
else if (state.detached_from) {
if (state.detached_at)
strbuf_addstr(&desc, HEAD_DETACHED_AT);
strbuf_addf(&desc, _("(HEAD detached at %s)"),
state.detached_from);
else
strbuf_addstr(&desc, HEAD_DETACHED_FROM);
strbuf_addstr(&desc, state.detached_from);
}
else
strbuf_addstr(&desc, _("no branch"));
strbuf_addch(&desc, ')');
strbuf_addf(&desc, _("(HEAD detached from %s)"),
state.detached_from);
} else
strbuf_addstr(&desc, _("(no branch)"));

wt_status_state_free_buffers(&state);
return strbuf_detach(&desc, NULL);
}

Expand Down Expand Up @@ -2350,25 +2341,43 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
return ret;
}

static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
{
if (!(a->kind ^ b->kind))
BUG("ref_kind_from_refname() should only mark one ref as HEAD");
if (a->kind & FILTER_REFS_DETACHED_HEAD)
return -1;
else if (b->kind & FILTER_REFS_DETACHED_HEAD)
return 1;
BUG("should have died in the xor check above");
return 0;
}

static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
{
struct atom_value *va, *vb;
int cmp;
int cmp_detached_head = 0;
cmp_type cmp_type = used_atom[s->atom].type;
int (*cmp_fn)(const char *, const char *);
struct strbuf err = STRBUF_INIT;

if (get_ref_atom_value(a, s->atom, &va, &err))
die("%s", err.buf);
if (get_ref_atom_value(b, s->atom, &vb, &err))
die("%s", err.buf);
strbuf_release(&err);
cmp_fn = s->ignore_case ? strcasecmp : strcmp;
if (s->version)
if (s->sort_flags & REF_SORTING_DETACHED_HEAD_FIRST &&
((a->kind | b->kind) & FILTER_REFS_DETACHED_HEAD)) {
cmp = compare_detached_head(a, b);
cmp_detached_head = 1;
} else if (s->sort_flags & REF_SORTING_VERSION) {
cmp = versioncmp(va->s, vb->s);
else if (cmp_type == FIELD_STR)
} else if (cmp_type == FIELD_STR) {
int (*cmp_fn)(const char *, const char *);
cmp_fn = s->sort_flags & REF_SORTING_ICASE
? strcasecmp : strcmp;
cmp = cmp_fn(va->s, vb->s);
else {
} else {
if (va->value < vb->value)
cmp = -1;
else if (va->value == vb->value)
Expand All @@ -2377,7 +2386,8 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
cmp = 1;
}

return (s->reverse) ? -cmp : cmp;
return (s->sort_flags & REF_SORTING_REVERSE && !cmp_detached_head)
? -cmp : cmp;
}

static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
Expand All @@ -2392,15 +2402,20 @@ static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
return cmp;
}
s = ref_sorting;
return s && s->ignore_case ?
return s && s->sort_flags & REF_SORTING_ICASE ?
strcasecmp(a->refname, b->refname) :
strcmp(a->refname, b->refname);
}

void ref_sorting_icase_all(struct ref_sorting *sorting, int flag)
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting,
unsigned int mask, int on)
{
for (; sorting; sorting = sorting->next)
sorting->ignore_case = !!flag;
for (; sorting; sorting = sorting->next) {
if (on)
sorting->sort_flags |= mask;
else
sorting->sort_flags &= ~mask;
}
}

void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
Expand Down Expand Up @@ -2537,12 +2552,12 @@ void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
*sorting_tail = s;

if (*arg == '-') {
s->reverse = 1;
s->sort_flags |= REF_SORTING_REVERSE;
arg++;
}
if (skip_prefix(arg, "version:", &arg) ||
skip_prefix(arg, "v:", &arg))
s->version = 1;
s->sort_flags |= REF_SORTING_VERSION;
s->atom = parse_sorting_atom(arg);
}

Expand Down
13 changes: 8 additions & 5 deletions ref-filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ struct atom_value;
struct ref_sorting {
struct ref_sorting *next;
int atom; /* index into used_atom array (internal) */
unsigned reverse : 1,
ignore_case : 1,
version : 1;
enum {
REF_SORTING_REVERSE = 1<<0,
REF_SORTING_ICASE = 1<<1,
REF_SORTING_VERSION = 1<<2,
REF_SORTING_DETACHED_HEAD_FIRST = 1<<3,
} sort_flags;
};

struct ref_array_item {
Expand Down Expand Up @@ -109,8 +112,8 @@ void ref_array_clear(struct ref_array *array);
int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
/* Set the ignore_case flag for all elements of a sorting list */
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag);
/* Set REF_SORTING_* sort_flags for all elements of a sorting list */
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
/* Based on the given format and quote_style, fill the strbuf */
int format_ref_array_item(struct ref_array_item *info,
const struct ref_format *format,
Expand Down
4 changes: 2 additions & 2 deletions refs/files-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1824,12 +1824,12 @@ static int create_symref_locked(struct files_ref_store *refs,

if (!fdopen_lock_file(&lock->lk, "w"))
return error("unable to fdopen %s: %s",
lock->lk.tempfile->filename.buf, strerror(errno));
get_lock_file_path(&lock->lk), strerror(errno));

update_symref_reflog(refs, lock, refname, target, logmsg);

/* no error check; commit_ref will check ferror */
fprintf(lock->lk.tempfile->fp, "ref: %s\n", target);
fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
if (commit_ref(lock) < 0)
return error("unable to write symref for %s: %s", refname,
strerror(errno));
Expand Down
6 changes: 4 additions & 2 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -1241,12 +1241,14 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
/*
* Have we seen the same patch id?
*/
id = has_commit_patch_id(commit, &ids);
id = patch_id_iter_first(commit, &ids);
if (!id)
continue;

commit->object.flags |= cherry_flag;
id->commit->object.flags |= cherry_flag;
do {
id->commit->object.flags |= cherry_flag;
} while ((id = patch_id_iter_next(id, &ids)));
}

free_patch_ids(&ids);
Expand Down
7 changes: 6 additions & 1 deletion submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,7 @@ static int get_next_submodule(struct child_process *cp,
strbuf_release(&submodule_prefix);
return 1;
} else {
struct strbuf empty_submodule_path = STRBUF_INIT;

fetch_task_release(task);
free(task);
Expand All @@ -1485,13 +1486,17 @@ static int get_next_submodule(struct child_process *cp,
* An empty directory is normal,
* the submodule is not initialized
*/
strbuf_addf(&empty_submodule_path, "%s/%s/",
spf->r->worktree,
ce->name);
if (S_ISGITLINK(ce->ce_mode) &&
!is_empty_dir(ce->name)) {
!is_empty_dir(empty_submodule_path.buf)) {
spf->result = 1;
strbuf_addf(err,
_("Could not access submodule '%s'\n"),
ce->name);
}
strbuf_release(&empty_submodule_path);
}
}

Expand Down
8 changes: 4 additions & 4 deletions t/annotate-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -483,12 +483,12 @@ test_expect_success 'setup -L :funcname with userdiff driver' '
echo "fortran-* diff=fortran" >.gitattributes &&
fortran_file=fortran-external-function &&
orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
cp $orig_file . &&
git add $fortran_file &&
cp "$orig_file" . &&
git add "$fortran_file" &&
GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
git commit -m "add fortran file" &&
sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >$fortran_file &&
git add $fortran_file &&
sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >"$fortran_file" &&
git add "$fortran_file" &&
GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
git commit -m "change fortran file"
'
Expand Down
16 changes: 8 additions & 8 deletions t/lib-submodule-update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,7 @@ test_submodule_switch_common () {
command="$1"
######################### Appearing submodule #########################
# Switching to a commit letting a submodule appear creates empty dir ...
if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
then
# Restoring stash fails to restore submodule index entry
RESULT="failure"
else
RESULT="success"
fi
test_expect_$RESULT "$command: added submodule creates empty directory" '
test_expect_success "$command: added submodule creates empty directory" '
prolog &&
reset_work_tree_to no_submodule &&
(
Expand All @@ -337,6 +330,13 @@ test_submodule_switch_common () {
)
'
# ... and doesn't care if it already exists.
if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
then
# Restoring stash fails to restore submodule index entry
RESULT="failure"
else
RESULT="success"
fi
test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
prolog &&
reset_work_tree_to no_submodule &&
Expand Down
12 changes: 8 additions & 4 deletions t/perf/p5303-many-packs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ repack_into_n () {
mkdir staging &&

git rev-list --first-parent HEAD |
sed -n '1~5p' |
head -n "$1" |
perl -e 'print reverse <>' \
>pushes
perl -e '
my $n = shift;
while (<>) {
last unless @commits < $n;
push @commits, $_ if $. % 5 == 1;
}
print reverse @commits;
' "$1" >pushes

# create base packfile
head -n 1 pushes |
Expand Down
7 changes: 6 additions & 1 deletion t/perf/p7519-fsmonitor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ setup_for_fsmonitor() {

git config core.fsmonitor "$INTEGRATION_SCRIPT" &&
git update-index --fsmonitor 2>error &&
test_must_be_empty error # ensure no silent error
if test_have_prereq WATCHMAN
then
test_must_be_empty error # ensure no silent error
else
grep "Empty last update token" error
fi
}

test_perf_w_drop_caches () {
Expand Down
6 changes: 6 additions & 0 deletions t/t0068-for-each-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ test_expect_success 'run based on configured value' '
grep again message
'

test_expect_success 'do nothing on empty config' '
# the whole thing would fail if for-each-ref iterated even
# once, because "git help --no-such-option" would fail
git for-each-repo --config=bogus.config -- help --no-such-option
'

test_done
72 changes: 32 additions & 40 deletions t/t1300-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,75 +12,75 @@ test_expect_success 'clear default config' '
'

cat > expect << EOF
[core]
[section]
penguin = little blue
EOF
test_expect_success 'initial' '
git config core.penguin "little blue" &&
git config section.penguin "little blue" &&
test_cmp expect .git/config
'

cat > expect << EOF
[core]
[section]
penguin = little blue
Movie = BadPhysics
EOF
test_expect_success 'mixed case' '
git config Core.Movie BadPhysics &&
git config Section.Movie BadPhysics &&
test_cmp expect .git/config
'

cat > expect << EOF
[core]
[section]
penguin = little blue
Movie = BadPhysics
[Cores]
[Sections]
WhatEver = Second
EOF
test_expect_success 'similar section' '
git config Cores.WhatEver Second &&
git config Sections.WhatEver Second &&
test_cmp expect .git/config
'

cat > expect << EOF
[core]
[section]
penguin = little blue
Movie = BadPhysics
UPPERCASE = true
[Cores]
[Sections]
WhatEver = Second
EOF
test_expect_success 'uppercase section' '
git config CORE.UPPERCASE true &&
git config SECTION.UPPERCASE true &&
test_cmp expect .git/config
'

test_expect_success 'replace with non-match' '
git config core.penguin kingpin !blue
git config section.penguin kingpin !blue
'

test_expect_success 'replace with non-match (actually matching)' '
git config core.penguin "very blue" !kingpin
git config section.penguin "very blue" !kingpin
'

cat > expect << EOF
[core]
[section]
penguin = very blue
Movie = BadPhysics
UPPERCASE = true
penguin = kingpin
[Cores]
[Sections]
WhatEver = Second
EOF

test_expect_success 'non-match result' 'test_cmp expect .git/config'

test_expect_success 'find mixed-case key by canonical name' '
test_cmp_config Second cores.whatever
test_cmp_config Second sections.whatever
'

test_expect_success 'find mixed-case key by non-canonical name' '
test_cmp_config Second CoReS.WhAtEvEr
test_cmp_config Second SeCtIoNs.WhAtEvEr
'

test_expect_success 'subsections are not canonicalized by git-config' '
Expand Down Expand Up @@ -469,7 +469,8 @@ test_expect_success 'new variable inserts into proper section' '
'

test_expect_success 'alternative --file (non-existing file should fail)' '
test_must_fail git config --file non-existing-config -l
test_must_fail git config --file non-existing-config -l &&
test_must_fail git config --file non-existing-config test.xyzzy
'

cat > other-config << EOF
Expand Down Expand Up @@ -506,10 +507,6 @@ test_expect_success 'editing stdin is an error' '

test_expect_success 'refer config from subdirectory' '
mkdir x &&
test_cmp_config -C x strasse --get --file ../other-config ein.bahn
'

test_expect_success 'refer config from subdirectory via --file' '
test_cmp_config -C x strasse --file=../other-config --get ein.bahn
'

Expand Down Expand Up @@ -1036,11 +1033,6 @@ test_expect_success SYMLINKS 'symlinked configuration' '
test_cmp expect actual
'

test_expect_success 'nonexistent configuration' '
test_must_fail git config --file=doesnotexist --list &&
test_must_fail git config --file=doesnotexist test.xyzzy
'

test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
ln -s doesnotexist linktonada &&
ln -s linktonada linktolinktonada &&
Expand All @@ -1065,12 +1057,12 @@ test_expect_success 'git -c "key=value" support' '
true
EOF
{
git -c core.name=value config core.name &&
git -c section.name=value config section.name &&
git -c foo.CamelCase=value config foo.camelcase &&
git -c foo.flag config --bool foo.flag
} >actual &&
test_cmp expect actual &&
test_must_fail git -c name=value config core.name
test_must_fail git -c name=value config section.name
'

# We just need a type-specifier here that cares about the
Expand Down Expand Up @@ -1115,7 +1107,7 @@ test_expect_success 'aliases can be CamelCased' '

test_expect_success 'git -c does not split values on equals' '
echo "value with = in it" >expect &&
git -c core.foo="value with = in it" config core.foo >actual &&
git -c section.foo="value with = in it" config section.foo >actual &&
test_cmp expect actual
'

Expand Down Expand Up @@ -1846,53 +1838,53 @@ do
done

cat >.git/config <<-\EOF &&
[core]
[section]
foo = true
number = 10
big = 1M
EOF

test_expect_success 'identical modern --type specifiers are allowed' '
test_cmp_config 1048576 --type=int --type=int core.big
test_cmp_config 1048576 --type=int --type=int section.big
'

test_expect_success 'identical legacy --type specifiers are allowed' '
test_cmp_config 1048576 --int --int core.big
test_cmp_config 1048576 --int --int section.big
'

test_expect_success 'identical mixed --type specifiers are allowed' '
test_cmp_config 1048576 --int --type=int core.big
test_cmp_config 1048576 --int --type=int section.big
'

test_expect_success 'non-identical modern --type specifiers are not allowed' '
test_must_fail git config --type=int --type=bool core.big 2>error &&
test_must_fail git config --type=int --type=bool section.big 2>error &&
test_i18ngrep "only one type at a time" error
'

test_expect_success 'non-identical legacy --type specifiers are not allowed' '
test_must_fail git config --int --bool core.big 2>error &&
test_must_fail git config --int --bool section.big 2>error &&
test_i18ngrep "only one type at a time" error
'

test_expect_success 'non-identical mixed --type specifiers are not allowed' '
test_must_fail git config --type=int --bool core.big 2>error &&
test_must_fail git config --type=int --bool section.big 2>error &&
test_i18ngrep "only one type at a time" error
'

test_expect_success '--type allows valid type specifiers' '
test_cmp_config true --type=bool core.foo
test_cmp_config true --type=bool section.foo
'

test_expect_success '--no-type unsets type specifiers' '
test_cmp_config 10 --type=bool --no-type core.number
test_cmp_config 10 --type=bool --no-type section.number
'

test_expect_success 'unset type specifiers may be reset to conflicting ones' '
test_cmp_config 1048576 --type=bool --no-type --type=int core.big
test_cmp_config 1048576 --type=bool --no-type --type=int section.big
'

test_expect_success '--type rejects unknown specifiers' '
test_must_fail git config --type=nonsense core.foo 2>error &&
test_must_fail git config --type=nonsense section.foo 2>error &&
test_i18ngrep "unrecognized --type argument" error
'

Expand Down
51 changes: 50 additions & 1 deletion t/t3203-branch-output.sh
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,63 @@ EOF
test_i18ncmp expect actual
'

test_expect_success 'git branch `--sort` option' '
test_expect_success 'git branch `--sort=[-]objectsize` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-two
branch-one
main
EOF
git branch --sort=objectsize >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
main
branch-two
EOF
git branch --sort=-objectsize >actual &&
test_i18ncmp expect actual
'

test_expect_success 'git branch `--sort=[-]type` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=type >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=-type >actual &&
test_i18ncmp expect actual
'

test_expect_success 'git branch `--sort=[-]version:refname` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=version:refname >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
main
branch-two
branch-one
EOF
git branch --sort=-version:refname >actual &&
test_i18ncmp expect actual
'

Expand Down
2 changes: 1 addition & 1 deletion t/t4129-apply-samemode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree
test_config core.sharedRepository 0666 &&
(
# Remove a default ACL if possible.
(setfacl -k newdir 2>/dev/null || true) &&
(setfacl -k . 2>/dev/null || true) &&
umask 0077 &&
# Test both files (f1) and leading dirs (d)
Expand Down
4 changes: 3 additions & 1 deletion t/t5516-fetch-push.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,9 @@ do
git cat-file commit $SHA1_2 &&
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
git fetch ../testrepo/.git $SHA1_3 2>err &&
test_i18ngrep "remote error:.*not our ref.*$SHA1_3\$" err
# ideally we would insist this be on a "remote error:"
# line, but it is racy; see the commit message
test_i18ngrep "not our ref.*$SHA1_3\$" err
)
'
done
Expand Down
117 changes: 117 additions & 0 deletions t/t5526-fetch-submodules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -722,4 +722,121 @@ test_expect_success 'fetch new submodule commit intermittently referenced by sup
)
'

add_commit_push () {
dir="$1" &&
msg="$2" &&
shift 2 &&
git -C "$dir" add "$@" &&
git -C "$dir" commit -a -m "$msg" &&
git -C "$dir" push
}

compare_refs_in_dir () {
fail= &&
if test "x$1" = 'x!'
then
fail='!' &&
shift
fi &&
git -C "$1" rev-parse --verify "$2" >expect &&
git -C "$3" rev-parse --verify "$4" >actual &&
eval $fail test_cmp expect actual
}


test_expect_success 'setup nested submodule fetch test' '
# does not depend on any previous test setups
for repo in outer middle inner
do
git init --bare $repo &&
git clone $repo ${repo}_content &&
echo "$repo" >"${repo}_content/file" &&
add_commit_push ${repo}_content "initial" file ||
return 1
done &&
git clone outer A &&
git -C A submodule add "$pwd/middle" &&
git -C A/middle/ submodule add "$pwd/inner" &&
add_commit_push A/middle/ "adding inner sub" .gitmodules inner &&
add_commit_push A/ "adding middle sub" .gitmodules middle &&
git clone outer B &&
git -C B/ submodule update --init middle &&
compare_refs_in_dir A HEAD B HEAD &&
compare_refs_in_dir A/middle HEAD B/middle HEAD &&
test_path_is_file B/file &&
test_path_is_file B/middle/file &&
test_path_is_missing B/middle/inner/file &&
echo "change on inner repo of A" >"A/middle/inner/file" &&
add_commit_push A/middle/inner "change on inner" file &&
add_commit_push A/middle "change on inner" inner &&
add_commit_push A "change on inner" middle
'

test_expect_success 'fetching a superproject containing an uninitialized sub/sub project' '
# depends on previous test for setup
git -C B/ fetch &&
compare_refs_in_dir A origin/HEAD B origin/HEAD
'

fetch_with_recursion_abort () {
# In a regression the following git call will run into infinite recursion.
# To handle that, we connect the sed command to the git call by a pipe
# so that sed can kill the infinite recursion when detected.
# The recursion creates git output like:
# Fetching submodule sub
# Fetching submodule sub/sub <-- [1]
# Fetching submodule sub/sub/sub
# ...
# [1] sed will stop reading and cause git to eventually stop and die

git -C "$1" fetch --recurse-submodules 2>&1 |
sed "/Fetching submodule $2[^$]/q" >out &&
! grep "Fetching submodule $2[^$]" out
}

test_expect_success 'setup recursive fetch with uninit submodule' '
# does not depend on any previous test setups
test_create_repo super &&
test_commit -C super initial &&
test_create_repo sub &&
test_commit -C sub initial &&
git -C sub rev-parse HEAD >expect &&
git -C super submodule add ../sub &&
git -C super commit -m "add sub" &&
git clone super superclone &&
git -C superclone submodule status >out &&
sed -e "s/^-//" -e "s/ sub.*$//" out >actual &&
test_cmp expect actual
'

test_expect_success 'recursive fetch with uninit submodule' '
# depends on previous test for setup
fetch_with_recursion_abort superclone sub &&
git -C superclone submodule status >out &&
sed -e "s/^-//" -e "s/ sub$//" out >actual &&
test_cmp expect actual
'

test_expect_success 'recursive fetch after deinit a submodule' '
# depends on previous test for setup
git -C superclone submodule update --init sub &&
git -C superclone submodule deinit -f sub &&
fetch_with_recursion_abort superclone sub &&
git -C superclone submodule status >out &&
sed -e "s/^-//" -e "s/ sub$//" out >actual &&
test_cmp expect actual
'

test_done
5 changes: 5 additions & 0 deletions t/t5570-git-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ test_expect_success 'fetch notices corrupt idx' '
)
'

test_expect_success 'client refuses to ask for repo with newline' '
test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr &&
test_i18ngrep newline.is.forbidden stderr
'

test_remote_error()
{
do_export=YesPlease
Expand Down
12 changes: 12 additions & 0 deletions t/t6007-rev-list-cherry-pick-file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,18 @@ test_expect_success '--count --left-right' '
test_cmp expect actual
'

test_expect_success '--cherry-pick with duplicates on each side' '
git checkout -b dup-orig &&
test_commit dup-base &&
git revert dup-base &&
git cherry-pick dup-base &&
git checkout -b dup-side HEAD~3 &&
test_tick &&
git cherry-pick -3 dup-orig &&
git rev-list --cherry-pick dup-orig...dup-side >actual &&
test_must_be_empty actual
'

# Corrupt the object store deliberately to make sure
# the object is not even checked for its existence.
remove_loose_object () {
Expand Down
354 changes: 167 additions & 187 deletions t/t6016-rev-list-graph-simplify-history.sh

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions t/t6030-bisect-porcelain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -926,14 +926,14 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
git bisect bad $HASH4 &&
git bisect reset &&
test -z "$(git for-each-ref "refs/bisect/*")" &&
test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" &&
test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" &&
test_path_is_missing "$GIT_DIR/BISECT_LOG" &&
test_path_is_missing "$GIT_DIR/BISECT_RUN" &&
test_path_is_missing "$GIT_DIR/BISECT_TERMS" &&
test_path_is_missing "$GIT_DIR/head-name" &&
test_path_is_missing "$GIT_DIR/BISECT_HEAD" &&
test_path_is_missing "$GIT_DIR/BISECT_START"
test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
test_path_is_missing ".git/BISECT_LOG" &&
test_path_is_missing ".git/BISECT_RUN" &&
test_path_is_missing ".git/BISECT_TERMS" &&
test_path_is_missing ".git/head-name" &&
test_path_is_missing ".git/BISECT_HEAD" &&
test_path_is_missing ".git/BISECT_START"
'

test_done
88 changes: 88 additions & 0 deletions t/t7012-skip-worktree-writing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,94 @@ test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '
--diff-filter=D -- keep-me.t
'

test_expect_success 'stash restore in sparse checkout' '
test_create_repo stash-restore &&
(
cd stash-restore &&
mkdir subdir &&
echo A >subdir/A &&
echo untouched >untouched &&
echo removeme >removeme &&
echo modified >modified &&
git add . &&
git commit -m Initial &&
echo AA >>subdir/A &&
echo addme >addme &&
echo tweaked >>modified &&
rm removeme &&
git add addme &&
git stash push &&
git sparse-checkout set subdir &&
# Ensure after sparse-checkout we only have expected files
cat >expect <<-EOF &&
S modified
S removeme
H subdir/A
S untouched
EOF
git ls-files -t >actual &&
test_cmp expect actual &&
test_path_is_missing addme &&
test_path_is_missing modified &&
test_path_is_missing removeme &&
test_path_is_file subdir/A &&
test_path_is_missing untouched &&
# Put a file in the working directory in the way
echo in the way >modified &&
git stash apply &&
# Ensure stash vivifies modifies paths...
cat >expect <<-EOF &&
H addme
H modified
H removeme
H subdir/A
S untouched
EOF
git ls-files -t >actual &&
test_cmp expect actual &&
# ...and that the paths show up in status as changed...
cat >expect <<-EOF &&
A addme
M modified
D removeme
M subdir/A
?? actual
?? expect
?? modified.stash.XXXXXX
EOF
git status --porcelain | \
sed -e s/stash......./stash.XXXXXX/ >actual &&
test_cmp expect actual &&
# ...and that working directory reflects the files correctly
test_path_is_file addme &&
test_path_is_file modified &&
test_path_is_missing removeme &&
test_path_is_file subdir/A &&
test_path_is_missing untouched &&
# ...including that we have the expected "modified" file...
cat >expect <<-EOF &&
modified
tweaked
EOF
test_cmp expect modified &&
# ...and that the other "modified" file is still present...
echo in the way >expect &&
test_cmp expect modified.stash.*
)
'

#TODO test_expect_failure 'git-apply adds file' false
#TODO test_expect_failure 'git-apply updates file' false
#TODO test_expect_failure 'git-apply removes file' false
Expand Down
15 changes: 15 additions & 0 deletions t/t7416-submodule-dash-url.sh
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,19 @@ test_expect_success 'fsck rejects embedded newline in relative url' '
grep gitmodulesUrl err
'

test_expect_success 'fsck rejects embedded newline in git url' '
git checkout --orphan git-newline &&
cat >.gitmodules <<-\EOF &&
[submodule "foo"]
url = "git://example.com:1234/repo%0a.git"
EOF
git add .gitmodules &&
git commit -m "git url with newline" &&
test_when_finished "rm -rf dst" &&
git init --bare dst &&
git -C dst config transfer.fsckObjects true &&
test_must_fail git push dst HEAD 2>err &&
grep gitmodulesUrl err
'

test_done
11 changes: 11 additions & 0 deletions t/t7610-mergetool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -828,4 +828,15 @@ test_expect_success 'mergetool -Oorder-file is honored' '
test_cmp expect actual
'

test_expect_success 'mergetool --tool-help shows recognized tools' '
# Check a few known tools are correctly shown
git mergetool --tool-help >mergetools &&
grep vimdiff mergetools &&
grep vimdiff3 mergetools &&
grep gvimdiff2 mergetools &&
grep araxis mergetools &&
grep xxdiff mergetools &&
grep meld mergetools
'

test_done
9 changes: 7 additions & 2 deletions t/test-lib-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,14 @@ test_chmod () {
git update-index --add "--chmod=$@"
}

# Get the modebits from a file or directory.
# Get the modebits from a file or directory, ignoring the setgid bit (g+s).
# This bit is inherited by subdirectories at their creation. So we remove it
# from the returning string to prevent callers from having to worry about the
# state of the bit in the test directory.
#
test_modebits () {
ls -ld "$1" | sed -e 's|^\(..........\).*|\1|'
ls -ld "$1" | sed -e 's|^\(..........\).*|\1|' \
-e 's|^\(......\)S|\1-|' -e 's|^\(......\)s|\1x|'
}

# Unset a configuration variable, but don't fail if it doesn't exist.
Expand Down
4 changes: 2 additions & 2 deletions wt-status.c
Original file line number Diff line number Diff line change
Expand Up @@ -1742,9 +1742,9 @@ static void wt_longstatus_print(struct wt_status *s)
} else if (s->state.detached_from) {
branch_name = s->state.detached_from;
if (s->state.detached_at)
on_what = HEAD_DETACHED_AT;
on_what = _("HEAD detached at ");
else
on_what = HEAD_DETACHED_FROM;
on_what = _("HEAD detached from ");
} else {
branch_name = "";
on_what = _("Not currently on any branch.");
Expand Down
2 changes: 0 additions & 2 deletions wt-status.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ enum wt_status_format {
STATUS_FORMAT_UNSPECIFIED
};

#define HEAD_DETACHED_AT _("HEAD detached at ")
#define HEAD_DETACHED_FROM _("HEAD detached from ")
#define SPARSE_CHECKOUT_DISABLED -1

struct wt_status_state {
Expand Down