зеркало из https://github.com/microsoft/git.git
Merge branch 'tr/notes-display'
* tr/notes-display: git-notes(1): add a section about the meaning of history notes: track whether notes_trees were changed at all notes: add shorthand --ref to override GIT_NOTES_REF commit --amend: copy notes to the new commit rebase: support automatic notes copying notes: implement helpers needed for note copying during rewrite notes: implement 'git notes copy --stdin' rebase -i: invoke post-rewrite hook rebase: invoke post-rewrite hook commit --amend: invoke post-rewrite hook Documentation: document post-rewrite hook Support showing notes from more than one notes tree test-lib: unset GIT_NOTES_REF to stop it from influencing tests Conflicts: git-am.sh refs.c
This commit is contained in:
Коммит
a86ed83cce
|
@ -519,10 +519,12 @@ check that makes sure that existing object files will not get overwritten.
|
|||
core.notesRef::
|
||||
When showing commit messages, also show notes which are stored in
|
||||
the given ref. This ref is expected to contain files named
|
||||
after the full SHA-1 of the commit they annotate.
|
||||
after the full SHA-1 of the commit they annotate. The ref
|
||||
must be fully qualified.
|
||||
+
|
||||
If such a file exists in the given ref, the referenced blob is read, and
|
||||
appended to the commit message, separated by a "Notes:" line. If the
|
||||
appended to the commit message, separated by a "Notes (<refname>):"
|
||||
line (shortened to "Notes:" in the case of "refs/notes/commits"). If the
|
||||
given ref itself does not exist, it is not an error, but means that no
|
||||
notes should be printed.
|
||||
+
|
||||
|
@ -1334,6 +1336,53 @@ mergetool.keepTemporaries::
|
|||
mergetool.prompt::
|
||||
Prompt before each invocation of the merge resolution program.
|
||||
|
||||
notes.displayRef::
|
||||
The (fully qualified) refname from which to show notes when
|
||||
showing commit messages. The value of this variable can be set
|
||||
to a glob, in which case notes from all matching refs will be
|
||||
shown. You may also specify this configuration variable
|
||||
several times. A warning will be issued for refs that do not
|
||||
exist, but a glob that does not match any refs is silently
|
||||
ignored.
|
||||
+
|
||||
This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`
|
||||
environment variable, which must be a colon separated list of refs or
|
||||
globs.
|
||||
+
|
||||
The effective value of "core.notesRef" (possibly overridden by
|
||||
GIT_NOTES_REF) is also implicitly added to the list of refs to be
|
||||
displayed.
|
||||
|
||||
notes.rewrite.<command>::
|
||||
When rewriting commits with <command> (currently `amend` or
|
||||
`rebase`) and this variable is set to `true`, git
|
||||
automatically copies your notes from the original to the
|
||||
rewritten commit. Defaults to `true`, but see
|
||||
"notes.rewriteRef" below.
|
||||
+
|
||||
This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
|
||||
environment variable, which must be a colon separated list of refs or
|
||||
globs.
|
||||
|
||||
notes.rewriteMode::
|
||||
When copying notes during a rewrite (see the
|
||||
"notes.rewrite.<command>" option), determines what to do if
|
||||
the target commit already has a note. Must be one of
|
||||
`overwrite`, `concatenate`, or `ignore`. Defaults to
|
||||
`concatenate`.
|
||||
+
|
||||
This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
|
||||
environment variable.
|
||||
|
||||
notes.rewriteRef::
|
||||
When copying notes during a rewrite, specifies the (fully
|
||||
qualified) ref whose notes should be copied. The ref may be a
|
||||
glob, in which case notes in all matching refs will be copied.
|
||||
You may also specify this configuration several times.
|
||||
+
|
||||
Does not have a default value; you must configure this variable to
|
||||
enable note rewriting.
|
||||
|
||||
pack.window::
|
||||
The size of the window used by linkgit:git-pack-objects[1] when no
|
||||
window size is given on the command line. Defaults to 10.
|
||||
|
|
|
@ -10,7 +10,7 @@ SYNOPSIS
|
|||
[verse]
|
||||
'git notes' [list [<object>]]
|
||||
'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
|
||||
'git notes' copy [-f] <from-object> <to-object>
|
||||
'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
|
||||
'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
|
||||
'git notes' edit [<object>]
|
||||
'git notes' show [<object>]
|
||||
|
@ -27,12 +27,17 @@ A typical use of notes is to extend a commit message without having
|
|||
to change the commit itself. Such commit notes can be shown by `git log`
|
||||
along with the original commit message. To discern these notes from the
|
||||
message stored in the commit object, the notes are indented like the
|
||||
message, after an unindented line saying "Notes:".
|
||||
message, after an unindented line saying "Notes (<refname>):" (or
|
||||
"Notes:" for the default setting).
|
||||
|
||||
To disable notes, you have to set the config variable core.notesRef to
|
||||
the empty string. Alternatively, you can set it to a different ref,
|
||||
something like "refs/notes/bugzilla". This setting can be overridden
|
||||
by the environment variable "GIT_NOTES_REF".
|
||||
This command always manipulates the notes specified in "core.notesRef"
|
||||
(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
|
||||
To change which notes are shown by 'git-log', see the
|
||||
"notes.displayRef" configuration.
|
||||
|
||||
See the description of "notes.rewrite.<command>" in
|
||||
linkgit:git-config[1] for a way of carrying your notes across commands
|
||||
that rewrite commits.
|
||||
|
||||
|
||||
SUBCOMMANDS
|
||||
|
@ -55,6 +60,16 @@ copy::
|
|||
object has none (use -f to overwrite existing notes to the
|
||||
second object). This subcommand is equivalent to:
|
||||
`git notes add [-f] -C $(git notes list <from-object>) <to-object>`
|
||||
+
|
||||
In `\--stdin` mode, take lines in the format
|
||||
+
|
||||
----------
|
||||
<from-object> SP <to-object> [ SP <rest> ] LF
|
||||
----------
|
||||
+
|
||||
on standard input, and copy the notes from each <from-object> to its
|
||||
corresponding <to-object>. (The optional `<rest>` is ignored so that
|
||||
the command can read the input given to the `post-rewrite` hook.)
|
||||
|
||||
append::
|
||||
Append to the notes of an existing object (defaults to HEAD).
|
||||
|
@ -101,6 +116,25 @@ OPTIONS
|
|||
Like '-C', but with '-c' the editor is invoked, so that
|
||||
the user can further edit the note message.
|
||||
|
||||
--ref <ref>::
|
||||
Manipulate the notes tree in <ref>. This overrides both
|
||||
GIT_NOTES_REF and the "core.notesRef" configuration. The ref
|
||||
is taken to be in `refs/notes/` if it is not qualified.
|
||||
|
||||
|
||||
NOTES
|
||||
-----
|
||||
|
||||
Every notes change creates a new commit at the specified notes ref.
|
||||
You can therefore inspect the history of the notes by invoking, e.g.,
|
||||
`git log -p notes/commits`.
|
||||
|
||||
Currently the commit message only records which operation triggered
|
||||
the update, and the commit authorship is determined according to the
|
||||
usual rules (see linkgit:git-commit[1]). These details may change in
|
||||
the future.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Johannes Schindelin <johannes.schindelin@gmx.de> and
|
||||
|
|
|
@ -317,6 +317,44 @@ This hook is invoked by 'git gc --auto'. It takes no parameter, and
|
|||
exiting with non-zero status from this script causes the 'git gc --auto'
|
||||
to abort.
|
||||
|
||||
post-rewrite
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by commands that rewrite commits (`git commit
|
||||
--amend`, 'git-rebase'; currently 'git-filter-branch' does 'not' call
|
||||
it!). Its first argument denotes the command it was invoked by:
|
||||
currently one of `amend` or `rebase`. Further command-dependent
|
||||
arguments may be passed in the future.
|
||||
|
||||
The hook receives a list of the rewritten commits on stdin, in the
|
||||
format
|
||||
|
||||
<old-sha1> SP <new-sha1> [ SP <extra-info> ] LF
|
||||
|
||||
The 'extra-info' is again command-dependent. If it is empty, the
|
||||
preceding SP is also omitted. Currently, no commands pass any
|
||||
'extra-info'.
|
||||
|
||||
The hook always runs after the automatic note copying (see
|
||||
"notes.rewrite.<command>" in linkgit:git-config.txt) has happened, and
|
||||
thus has access to these notes.
|
||||
|
||||
The following command-specific comments apply:
|
||||
|
||||
rebase::
|
||||
For the 'squash' and 'fixup' operation, all commits that were
|
||||
squashed are listed as being rewritten to the squashed commit.
|
||||
This means that there will be several lines sharing the same
|
||||
'new-sha1'.
|
||||
+
|
||||
The commits are guaranteed to be listed in the order that they were
|
||||
processed by rebase.
|
||||
|
||||
There is no default 'post-rewrite' hook, but see the
|
||||
`post-receive-copy-notes` script in `contrib/hooks` for an example
|
||||
that copies your git-notes to the rewritten commits.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
|
|
@ -30,9 +30,18 @@ people using 80-column terminals.
|
|||
defaults to UTF-8.
|
||||
|
||||
--no-notes::
|
||||
--show-notes::
|
||||
--show-notes[=<ref>]::
|
||||
Show the notes (see linkgit:git-notes[1]) that annotate the
|
||||
commit, when showing the commit log message. This is the default
|
||||
for `git log`, `git show` and `git whatchanged` commands when
|
||||
there is no `--pretty`, `--format` nor `--oneline` option is
|
||||
given on the command line.
|
||||
+
|
||||
With an optional argument, add this ref to the list of notes. The ref
|
||||
is taken to be in `refs/notes/` if it is not qualified.
|
||||
|
||||
--[no-]standard-notes::
|
||||
Enable or disable populating the notes ref list from the
|
||||
'core.notesRef' and 'notes.displayRef' variables (or
|
||||
corresponding environment overrides). Enabled by default.
|
||||
See linkgit:git-config[1].
|
||||
|
|
17
builtin.h
17
builtin.h
|
@ -20,6 +20,23 @@ extern int commit_tree(const char *msg, unsigned char *tree,
|
|||
struct commit_list *parents, unsigned char *ret,
|
||||
const char *author);
|
||||
extern int commit_notes(struct notes_tree *t, const char *msg);
|
||||
|
||||
struct notes_rewrite_cfg {
|
||||
struct notes_tree **trees;
|
||||
const char *cmd;
|
||||
int enabled;
|
||||
combine_notes_fn *combine;
|
||||
struct string_list *refs;
|
||||
int refs_from_env;
|
||||
int mode_from_env;
|
||||
};
|
||||
|
||||
combine_notes_fn *parse_combine_notes_fn(const char *v);
|
||||
struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
|
||||
int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
|
||||
const unsigned char *from_obj, const unsigned char *to_obj);
|
||||
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
|
||||
|
||||
extern int check_pager_config(const char *cmd);
|
||||
|
||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
|
|
|
@ -66,6 +66,7 @@ static char *edit_message, *use_message;
|
|||
static char *author_name, *author_email, *author_date;
|
||||
static int all, edit_flag, also, interactive, only, amend, signoff;
|
||||
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
|
||||
static int no_post_rewrite;
|
||||
static char *untracked_files_arg, *force_date;
|
||||
/*
|
||||
* The default commit message cleanup mode will remove the lines
|
||||
|
@ -137,6 +138,7 @@ static struct option builtin_commit_options[] = {
|
|||
OPT_BOOLEAN('z', "null", &null_termination,
|
||||
"terminate entries with NUL"),
|
||||
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
|
||||
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
|
||||
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
|
||||
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
|
||||
/* end commit contents options */
|
||||
|
@ -1160,6 +1162,40 @@ static int git_commit_config(const char *k, const char *v, void *cb)
|
|||
return git_status_config(k, v, s);
|
||||
}
|
||||
|
||||
static const char post_rewrite_hook[] = "hooks/post-rewrite";
|
||||
|
||||
static int run_rewrite_hook(const unsigned char *oldsha1,
|
||||
const unsigned char *newsha1)
|
||||
{
|
||||
/* oldsha1 SP newsha1 LF NUL */
|
||||
static char buf[2*40 + 3];
|
||||
struct child_process proc;
|
||||
const char *argv[3];
|
||||
int code;
|
||||
size_t n;
|
||||
|
||||
if (access(git_path(post_rewrite_hook), X_OK) < 0)
|
||||
return 0;
|
||||
|
||||
argv[0] = git_path(post_rewrite_hook);
|
||||
argv[1] = "amend";
|
||||
argv[2] = NULL;
|
||||
|
||||
memset(&proc, 0, sizeof(proc));
|
||||
proc.argv = argv;
|
||||
proc.in = -1;
|
||||
proc.stdout_to_stderr = 1;
|
||||
|
||||
code = start_command(&proc);
|
||||
if (code)
|
||||
return code;
|
||||
n = snprintf(buf, sizeof(buf), "%s %s\n",
|
||||
sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
|
||||
write_in_full(proc.in, buf, n);
|
||||
close(proc.in);
|
||||
return finish_command(&proc);
|
||||
}
|
||||
|
||||
int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
@ -1303,6 +1339,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
|||
|
||||
rerere(0);
|
||||
run_hook(get_index_file(), "post-commit", NULL);
|
||||
if (amend && !no_post_rewrite) {
|
||||
struct notes_rewrite_cfg *cfg;
|
||||
cfg = init_copy_notes_for_rewrite("amend");
|
||||
if (cfg) {
|
||||
copy_note_for_rewrite(cfg, head_sha1, commit_sha1);
|
||||
finish_copy_notes_for_rewrite(cfg);
|
||||
}
|
||||
run_rewrite_hook(head_sha1, commit_sha1);
|
||||
}
|
||||
if (!quiet)
|
||||
print_summary(prefix, commit_sha1);
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
|
|||
|
||||
if (!rev->show_notes_given && !rev->pretty_given)
|
||||
rev->show_notes = 1;
|
||||
if (rev->show_notes)
|
||||
init_display_notes(&rev->notes_opt);
|
||||
|
||||
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
||||
rev->always_show_header = 0;
|
||||
|
@ -1105,6 +1107,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
|||
if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
|
||||
DIFF_OPT_SET(&rev.diffopt, BINARY);
|
||||
|
||||
if (rev.show_notes)
|
||||
init_display_notes(&rev.notes_opt);
|
||||
|
||||
if (!use_stdout)
|
||||
output_directory = set_outdir(prefix, output_directory);
|
||||
|
||||
|
|
197
builtin/notes.c
197
builtin/notes.c
|
@ -16,6 +16,7 @@
|
|||
#include "exec_cmd.h"
|
||||
#include "run-command.h"
|
||||
#include "parse-options.h"
|
||||
#include "string-list.h"
|
||||
|
||||
static const char * const git_notes_usage[] = {
|
||||
"git notes [list [<object>]]",
|
||||
|
@ -239,6 +240,8 @@ int commit_notes(struct notes_tree *t, const char *msg)
|
|||
t = &default_notes_tree;
|
||||
if (!t->initialized || !t->ref || !*t->ref)
|
||||
die("Cannot commit uninitialized/unreferenced notes tree");
|
||||
if (!t->dirty)
|
||||
return 0; /* don't have to commit an unchanged tree */
|
||||
|
||||
/* Prepare commit message and reflog message */
|
||||
strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */
|
||||
|
@ -269,6 +272,161 @@ int commit_notes(struct notes_tree *t, const char *msg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
combine_notes_fn *parse_combine_notes_fn(const char *v)
|
||||
{
|
||||
if (!strcasecmp(v, "overwrite"))
|
||||
return combine_notes_overwrite;
|
||||
else if (!strcasecmp(v, "ignore"))
|
||||
return combine_notes_ignore;
|
||||
else if (!strcasecmp(v, "concatenate"))
|
||||
return combine_notes_concatenate;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int notes_rewrite_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
struct notes_rewrite_cfg *c = cb;
|
||||
if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
|
||||
c->enabled = git_config_bool(k, v);
|
||||
return 0;
|
||||
} else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
|
||||
if (!v)
|
||||
config_error_nonbool(k);
|
||||
c->combine = parse_combine_notes_fn(v);
|
||||
if (!c->combine) {
|
||||
error("Bad notes.rewriteMode value: '%s'", v);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
|
||||
/* note that a refs/ prefix is implied in the
|
||||
* underlying for_each_glob_ref */
|
||||
if (!prefixcmp(v, "refs/notes/"))
|
||||
string_list_add_refs_by_glob(c->refs, v);
|
||||
else
|
||||
warning("Refusing to rewrite notes in %s"
|
||||
" (outside of refs/notes/)", v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
|
||||
{
|
||||
struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
|
||||
const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
|
||||
const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
|
||||
c->cmd = cmd;
|
||||
c->enabled = 1;
|
||||
c->combine = combine_notes_concatenate;
|
||||
c->refs = xcalloc(1, sizeof(struct string_list));
|
||||
c->refs->strdup_strings = 1;
|
||||
c->refs_from_env = 0;
|
||||
c->mode_from_env = 0;
|
||||
if (rewrite_mode_env) {
|
||||
c->mode_from_env = 1;
|
||||
c->combine = parse_combine_notes_fn(rewrite_mode_env);
|
||||
if (!c->combine)
|
||||
error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT
|
||||
" value: '%s'", rewrite_mode_env);
|
||||
}
|
||||
if (rewrite_refs_env) {
|
||||
c->refs_from_env = 1;
|
||||
string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
|
||||
}
|
||||
git_config(notes_rewrite_config, c);
|
||||
if (!c->enabled || !c->refs->nr) {
|
||||
string_list_clear(c->refs, 0);
|
||||
free(c->refs);
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->trees = load_notes_trees(c->refs);
|
||||
string_list_clear(c->refs, 0);
|
||||
free(c->refs);
|
||||
return c;
|
||||
}
|
||||
|
||||
int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
|
||||
const unsigned char *from_obj, const unsigned char *to_obj)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
for (i = 0; c->trees[i]; i++)
|
||||
ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; c->trees[i]; i++) {
|
||||
commit_notes(c->trees[i], "Notes added by 'git notes copy'");
|
||||
free_notes(c->trees[i]);
|
||||
}
|
||||
free(c->trees);
|
||||
free(c);
|
||||
}
|
||||
|
||||
int notes_copy_from_stdin(int force, const char *rewrite_cmd)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct notes_rewrite_cfg *c = NULL;
|
||||
struct notes_tree *t;
|
||||
int ret = 0;
|
||||
|
||||
if (rewrite_cmd) {
|
||||
c = init_copy_notes_for_rewrite(rewrite_cmd);
|
||||
if (!c)
|
||||
return 0;
|
||||
} else {
|
||||
init_notes(NULL, NULL, NULL, 0);
|
||||
t = &default_notes_tree;
|
||||
}
|
||||
|
||||
while (strbuf_getline(&buf, stdin, '\n') != EOF) {
|
||||
unsigned char from_obj[20], to_obj[20];
|
||||
struct strbuf **split;
|
||||
int err;
|
||||
|
||||
split = strbuf_split(&buf, ' ');
|
||||
if (!split[0] || !split[1])
|
||||
die("Malformed input line: '%s'.", buf.buf);
|
||||
strbuf_rtrim(split[0]);
|
||||
strbuf_rtrim(split[1]);
|
||||
if (get_sha1(split[0]->buf, from_obj))
|
||||
die("Failed to resolve '%s' as a valid ref.", split[0]->buf);
|
||||
if (get_sha1(split[1]->buf, to_obj))
|
||||
die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
|
||||
|
||||
if (rewrite_cmd)
|
||||
err = copy_note_for_rewrite(c, from_obj, to_obj);
|
||||
else
|
||||
err = copy_note(t, from_obj, to_obj, force,
|
||||
combine_notes_overwrite);
|
||||
|
||||
if (err) {
|
||||
error("Failed to copy notes from '%s' to '%s'",
|
||||
split[0]->buf, split[1]->buf);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
strbuf_list_free(split);
|
||||
}
|
||||
|
||||
if (!rewrite_cmd) {
|
||||
commit_notes(t, "Notes added by 'git notes copy'");
|
||||
free_notes(t);
|
||||
} else {
|
||||
finish_copy_notes_for_rewrite(c);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_notes(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct notes_tree *t;
|
||||
|
@ -278,9 +436,11 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
|||
char logmsg[100];
|
||||
|
||||
int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
|
||||
remove = 0, prune = 0, force = 0;
|
||||
remove = 0, prune = 0, force = 0, from_stdin = 0;
|
||||
int given_object = 0, i = 1, retval = 0;
|
||||
struct msg_arg msg = { 0, 0, STRBUF_INIT };
|
||||
const char *rewrite_cmd = NULL;
|
||||
const char *override_notes_ref = NULL;
|
||||
struct option options[] = {
|
||||
OPT_GROUP("Notes contents options"),
|
||||
{ OPTION_CALLBACK, 'm', "message", &msg, "MSG",
|
||||
|
@ -297,6 +457,11 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
|||
parse_reuse_arg},
|
||||
OPT_GROUP("Other options"),
|
||||
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
|
||||
OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
|
||||
OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
|
||||
"use notes from <notes_ref>"),
|
||||
OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
|
||||
"load rewriting config for <command> (implies --stdin)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -304,6 +469,19 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
|||
|
||||
argc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);
|
||||
|
||||
if (override_notes_ref) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
if (!prefixcmp(override_notes_ref, "refs/notes/"))
|
||||
/* we're happy */;
|
||||
else if (!prefixcmp(override_notes_ref, "notes/"))
|
||||
strbuf_addstr(&sb, "refs/");
|
||||
else
|
||||
strbuf_addstr(&sb, "refs/notes/");
|
||||
strbuf_addstr(&sb, override_notes_ref);
|
||||
setenv("GIT_NOTES_REF", sb.buf, 1);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
if (argc && !strcmp(argv[0], "list"))
|
||||
list = 1;
|
||||
else if (argc && !strcmp(argv[0], "add"))
|
||||
|
@ -345,8 +523,25 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
|||
usage_with_options(git_notes_usage, options);
|
||||
}
|
||||
|
||||
if (!copy && rewrite_cmd) {
|
||||
error("cannot use --for-rewrite with %s subcommand.", argv[0]);
|
||||
usage_with_options(git_notes_usage, options);
|
||||
}
|
||||
if (!copy && from_stdin) {
|
||||
error("cannot use --stdin with %s subcommand.", argv[0]);
|
||||
usage_with_options(git_notes_usage, options);
|
||||
}
|
||||
|
||||
if (copy) {
|
||||
const char *from_ref;
|
||||
if (from_stdin || rewrite_cmd) {
|
||||
if (argc > 1) {
|
||||
error("too many parameters");
|
||||
usage_with_options(git_notes_usage, options);
|
||||
} else {
|
||||
return notes_copy_from_stdin(force, rewrite_cmd);
|
||||
}
|
||||
}
|
||||
if (argc < 3) {
|
||||
error("too few parameters");
|
||||
usage_with_options(git_notes_usage, options);
|
||||
|
|
3
cache.h
3
cache.h
|
@ -387,6 +387,9 @@ static inline enum object_type object_type(unsigned int mode)
|
|||
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
|
||||
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
|
||||
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
|
||||
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
|
||||
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
|
||||
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
|
||||
|
||||
/*
|
||||
* Repository-local GIT_* environment variables
|
||||
|
|
13
git-am.sh
13
git-am.sh
|
@ -593,6 +593,7 @@ do
|
|||
echo "Patch is empty. Was it split wrong?"
|
||||
stop_here $this
|
||||
}
|
||||
rm -f "$dotest/original-commit"
|
||||
if test -f "$dotest/rebasing" &&
|
||||
commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
|
||||
-e q "$dotest/$msgnum") &&
|
||||
|
@ -600,6 +601,7 @@ do
|
|||
then
|
||||
git cat-file commit "$commit" |
|
||||
sed -e '1,/^$/d' >"$dotest/msg-clean"
|
||||
echo "$commit" > "$dotest/original-commit"
|
||||
else
|
||||
{
|
||||
sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
|
||||
|
@ -783,6 +785,10 @@ do
|
|||
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
|
||||
stop_here $this
|
||||
|
||||
if test -f "$dotest/original-commit"; then
|
||||
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
|
||||
fi
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/post-applypatch
|
||||
then
|
||||
"$GIT_DIR"/hooks/post-applypatch
|
||||
|
@ -791,5 +797,12 @@ do
|
|||
go_next
|
||||
done
|
||||
|
||||
if test -s "$dotest"/rewritten; then
|
||||
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
|
||||
if test -x "$GIT_DIR"/hooks/post-rewrite; then
|
||||
"$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -fr "$dotest"
|
||||
git gc --auto
|
||||
|
|
|
@ -96,6 +96,13 @@ AUTHOR_SCRIPT="$DOTEST"/author-script
|
|||
# command is processed, this file is deleted.
|
||||
AMEND="$DOTEST"/amend
|
||||
|
||||
# For the post-rewrite hook, we make a list of rewritten commits and
|
||||
# their new sha1s. The rewritten-pending list keeps the sha1s of
|
||||
# commits that have been processed, but not committed yet,
|
||||
# e.g. because they are waiting for a 'squash' command.
|
||||
REWRITTEN_LIST="$DOTEST"/rewritten-list
|
||||
REWRITTEN_PENDING="$DOTEST"/rewritten-pending
|
||||
|
||||
PRESERVE_MERGES=
|
||||
STRATEGY=
|
||||
ONTO=
|
||||
|
@ -198,6 +205,7 @@ make_patch () {
|
|||
}
|
||||
|
||||
die_with_patch () {
|
||||
echo "$1" > "$DOTEST"/stopped-sha
|
||||
make_patch "$1"
|
||||
git rerere
|
||||
die "$2"
|
||||
|
@ -348,6 +356,7 @@ pick_one_preserving_merges () {
|
|||
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
|
||||
die_with_patch $sha1 "Error redoing merge $sha1"
|
||||
fi
|
||||
echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST"
|
||||
;;
|
||||
*)
|
||||
output git cherry-pick "$@" ||
|
||||
|
@ -425,6 +434,26 @@ die_failed_squash() {
|
|||
die_with_patch $1 ""
|
||||
}
|
||||
|
||||
flush_rewritten_pending() {
|
||||
test -s "$REWRITTEN_PENDING" || return
|
||||
newsha1="$(git rev-parse HEAD^0)"
|
||||
sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST"
|
||||
rm -f "$REWRITTEN_PENDING"
|
||||
}
|
||||
|
||||
record_in_rewritten() {
|
||||
oldsha1="$(git rev-parse $1)"
|
||||
echo "$oldsha1" >> "$REWRITTEN_PENDING"
|
||||
|
||||
case "$(peek_next_command)" in
|
||||
squash|s|fixup|f)
|
||||
;;
|
||||
*)
|
||||
flush_rewritten_pending
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
do_next () {
|
||||
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
|
||||
read command sha1 rest < "$TODO"
|
||||
|
@ -438,6 +467,7 @@ do_next () {
|
|||
mark_action_done
|
||||
pick_one $sha1 ||
|
||||
die_with_patch $sha1 "Could not apply $sha1... $rest"
|
||||
record_in_rewritten $sha1
|
||||
;;
|
||||
reword|r)
|
||||
comment_for_reflog reword
|
||||
|
@ -445,7 +475,8 @@ do_next () {
|
|||
mark_action_done
|
||||
pick_one $sha1 ||
|
||||
die_with_patch $sha1 "Could not apply $sha1... $rest"
|
||||
git commit --amend
|
||||
git commit --amend --no-post-rewrite
|
||||
record_in_rewritten $sha1
|
||||
;;
|
||||
edit|e)
|
||||
comment_for_reflog edit
|
||||
|
@ -453,6 +484,7 @@ do_next () {
|
|||
mark_action_done
|
||||
pick_one $sha1 ||
|
||||
die_with_patch $sha1 "Could not apply $sha1... $rest"
|
||||
echo "$1" > "$DOTEST"/stopped-sha
|
||||
make_patch $sha1
|
||||
git rev-parse --verify HEAD > "$AMEND"
|
||||
warn "Stopped at $sha1... $rest"
|
||||
|
@ -509,6 +541,7 @@ do_next () {
|
|||
rm -f "$SQUASH_MSG" "$FIXUP_MSG"
|
||||
;;
|
||||
esac
|
||||
record_in_rewritten $sha1
|
||||
;;
|
||||
*)
|
||||
warn "Unknown command: $command $sha1 $rest"
|
||||
|
@ -537,6 +570,15 @@ do_next () {
|
|||
test ! -f "$DOTEST"/verbose ||
|
||||
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
|
||||
} &&
|
||||
{
|
||||
git notes copy --for-rewrite=rebase < "$REWRITTEN_LIST" ||
|
||||
true # we don't care if this copying failed
|
||||
} &&
|
||||
if test -x "$GIT_DIR"/hooks/post-rewrite &&
|
||||
test -s "$REWRITTEN_LIST"; then
|
||||
"$GIT_DIR"/hooks/post-rewrite rebase < "$REWRITTEN_LIST"
|
||||
true # we don't care if this hook failed
|
||||
fi &&
|
||||
rm -rf "$DOTEST" &&
|
||||
git gc --auto &&
|
||||
warn "Successfully rebased and updated $HEADNAME."
|
||||
|
@ -571,7 +613,12 @@ skip_unnecessary_picks () {
|
|||
esac
|
||||
echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
|
||||
done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
|
||||
mv -f "$TODO".new "$TODO" ||
|
||||
mv -f "$TODO".new "$TODO" &&
|
||||
case "$(peek_next_command)" in
|
||||
squash|s|fixup|f)
|
||||
record_in_rewritten "$ONTO"
|
||||
;;
|
||||
esac ||
|
||||
die "Could not skip unnecessary pick commands"
|
||||
}
|
||||
|
||||
|
@ -685,6 +732,7 @@ first and then run 'git rebase --continue' again."
|
|||
test -n "$amend" && git reset --soft $amend
|
||||
die "Could not commit staged changes."
|
||||
}
|
||||
record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
|
||||
fi
|
||||
|
||||
require_clean_work_tree
|
||||
|
|
|
@ -79,6 +79,7 @@ continue_merge () {
|
|||
then
|
||||
printf "Committed: %0${prec}d " $msgnum
|
||||
fi
|
||||
echo "$cmt $(git rev-parse HEAD^0)" >> "$dotest/rewritten"
|
||||
else
|
||||
if test -z "$GIT_QUIET"
|
||||
then
|
||||
|
@ -151,6 +152,11 @@ move_to_original_branch () {
|
|||
|
||||
finish_rb_merge () {
|
||||
move_to_original_branch
|
||||
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
|
||||
if test -x "$GIT_DIR"/hooks/post-rewrite &&
|
||||
test -s "$dotest"/rewritten; then
|
||||
"$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
|
||||
fi
|
||||
rm -r "$dotest"
|
||||
say All done.
|
||||
}
|
||||
|
|
190
notes.c
190
notes.c
|
@ -5,6 +5,8 @@
|
|||
#include "utf8.h"
|
||||
#include "strbuf.h"
|
||||
#include "tree-walk.h"
|
||||
#include "string-list.h"
|
||||
#include "refs.h"
|
||||
|
||||
/*
|
||||
* Use a non-balancing simple 16-tree structure with struct int_node as
|
||||
|
@ -68,6 +70,9 @@ struct non_note {
|
|||
|
||||
struct notes_tree default_notes_tree;
|
||||
|
||||
static struct string_list display_notes_refs;
|
||||
static struct notes_tree **display_notes_trees;
|
||||
|
||||
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
|
||||
struct int_node *node, unsigned int n);
|
||||
|
||||
|
@ -828,6 +833,83 @@ int combine_notes_ignore(unsigned char *cur_sha1,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
|
||||
int flag, void *cb)
|
||||
{
|
||||
struct string_list *refs = cb;
|
||||
if (!unsorted_string_list_has_string(refs, path))
|
||||
string_list_append(path, refs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
|
||||
{
|
||||
if (has_glob_specials(glob)) {
|
||||
for_each_glob_ref(string_list_add_one_ref, glob, list);
|
||||
} else {
|
||||
unsigned char sha1[20];
|
||||
if (get_sha1(glob, sha1))
|
||||
warning("notes ref %s is invalid", glob);
|
||||
if (!unsorted_string_list_has_string(list, glob))
|
||||
string_list_append(glob, list);
|
||||
}
|
||||
}
|
||||
|
||||
void string_list_add_refs_from_colon_sep(struct string_list *list,
|
||||
const char *globs)
|
||||
{
|
||||
struct strbuf globbuf = STRBUF_INIT;
|
||||
struct strbuf **split;
|
||||
int i;
|
||||
|
||||
strbuf_addstr(&globbuf, globs);
|
||||
split = strbuf_split(&globbuf, ':');
|
||||
|
||||
for (i = 0; split[i]; i++) {
|
||||
if (!split[i]->len)
|
||||
continue;
|
||||
if (split[i]->buf[split[i]->len-1] == ':')
|
||||
strbuf_setlen(split[i], split[i]->len-1);
|
||||
string_list_add_refs_by_glob(list, split[i]->buf);
|
||||
}
|
||||
|
||||
strbuf_list_free(split);
|
||||
strbuf_release(&globbuf);
|
||||
}
|
||||
|
||||
static int string_list_add_refs_from_list(struct string_list_item *item,
|
||||
void *cb)
|
||||
{
|
||||
struct string_list *list = cb;
|
||||
string_list_add_refs_by_glob(list, item->string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int notes_display_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
int *load_refs = cb;
|
||||
|
||||
if (*load_refs && !strcmp(k, "notes.displayref")) {
|
||||
if (!v)
|
||||
config_error_nonbool(k);
|
||||
string_list_add_refs_by_glob(&display_notes_refs, v);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *default_notes_ref(void)
|
||||
{
|
||||
const char *notes_ref = NULL;
|
||||
if (!notes_ref)
|
||||
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
|
||||
if (!notes_ref)
|
||||
notes_ref = notes_ref_name; /* value of core.notesRef config */
|
||||
if (!notes_ref)
|
||||
notes_ref = GIT_NOTES_DEFAULT_REF;
|
||||
return notes_ref;
|
||||
}
|
||||
|
||||
void init_notes(struct notes_tree *t, const char *notes_ref,
|
||||
combine_notes_fn combine_notes, int flags)
|
||||
{
|
||||
|
@ -840,11 +922,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
|
|||
assert(!t->initialized);
|
||||
|
||||
if (!notes_ref)
|
||||
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
|
||||
if (!notes_ref)
|
||||
notes_ref = notes_ref_name; /* value of core.notesRef config */
|
||||
if (!notes_ref)
|
||||
notes_ref = GIT_NOTES_DEFAULT_REF;
|
||||
notes_ref = default_notes_ref();
|
||||
|
||||
if (!combine_notes)
|
||||
combine_notes = combine_notes_concatenate;
|
||||
|
@ -855,6 +933,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
|
|||
t->ref = notes_ref ? xstrdup(notes_ref) : NULL;
|
||||
t->combine_notes = combine_notes;
|
||||
t->initialized = 1;
|
||||
t->dirty = 0;
|
||||
|
||||
if (flags & NOTES_INIT_EMPTY || !notes_ref ||
|
||||
read_ref(notes_ref, object_sha1))
|
||||
|
@ -868,6 +947,63 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
|
|||
load_subtree(t, &root_tree, t->root, 0);
|
||||
}
|
||||
|
||||
struct load_notes_cb_data {
|
||||
int counter;
|
||||
struct notes_tree **trees;
|
||||
};
|
||||
|
||||
static int load_one_display_note_ref(struct string_list_item *item,
|
||||
void *cb_data)
|
||||
{
|
||||
struct load_notes_cb_data *c = cb_data;
|
||||
struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
|
||||
init_notes(t, item->string, combine_notes_ignore, 0);
|
||||
c->trees[c->counter++] = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct notes_tree **load_notes_trees(struct string_list *refs)
|
||||
{
|
||||
struct notes_tree **trees;
|
||||
struct load_notes_cb_data cb_data;
|
||||
trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
|
||||
cb_data.counter = 0;
|
||||
cb_data.trees = trees;
|
||||
for_each_string_list(load_one_display_note_ref, refs, &cb_data);
|
||||
trees[cb_data.counter] = NULL;
|
||||
return trees;
|
||||
}
|
||||
|
||||
void init_display_notes(struct display_notes_opt *opt)
|
||||
{
|
||||
char *display_ref_env;
|
||||
int load_config_refs = 0;
|
||||
display_notes_refs.strdup_strings = 1;
|
||||
|
||||
assert(!display_notes_trees);
|
||||
|
||||
if (!opt || !opt->suppress_default_notes) {
|
||||
string_list_append(default_notes_ref(), &display_notes_refs);
|
||||
display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
|
||||
if (display_ref_env) {
|
||||
string_list_add_refs_from_colon_sep(&display_notes_refs,
|
||||
display_ref_env);
|
||||
load_config_refs = 0;
|
||||
} else
|
||||
load_config_refs = 1;
|
||||
}
|
||||
|
||||
git_config(notes_display_config, &load_config_refs);
|
||||
|
||||
if (opt && opt->extra_notes_refs)
|
||||
for_each_string_list(string_list_add_refs_from_list,
|
||||
opt->extra_notes_refs,
|
||||
&display_notes_refs);
|
||||
|
||||
display_notes_trees = load_notes_trees(&display_notes_refs);
|
||||
string_list_clear(&display_notes_refs, 0);
|
||||
}
|
||||
|
||||
void add_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
const unsigned char *note_sha1, combine_notes_fn combine_notes)
|
||||
{
|
||||
|
@ -876,6 +1012,7 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,
|
|||
if (!t)
|
||||
t = &default_notes_tree;
|
||||
assert(t->initialized);
|
||||
t->dirty = 1;
|
||||
if (!combine_notes)
|
||||
combine_notes = t->combine_notes;
|
||||
l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node));
|
||||
|
@ -891,6 +1028,7 @@ void remove_note(struct notes_tree *t, const unsigned char *object_sha1)
|
|||
if (!t)
|
||||
t = &default_notes_tree;
|
||||
assert(t->initialized);
|
||||
t->dirty = 1;
|
||||
hashcpy(l.key_sha1, object_sha1);
|
||||
hashclr(l.val_sha1);
|
||||
note_tree_remove(t, t->root, 0, &l);
|
||||
|
@ -1016,8 +1154,18 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
|||
if (msglen && msg[msglen - 1] == '\n')
|
||||
msglen--;
|
||||
|
||||
if (flags & NOTES_SHOW_HEADER)
|
||||
strbuf_addstr(sb, "\nNotes:\n");
|
||||
if (flags & NOTES_SHOW_HEADER) {
|
||||
const char *ref = t->ref;
|
||||
if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
|
||||
strbuf_addstr(sb, "\nNotes:\n");
|
||||
} else {
|
||||
if (!prefixcmp(ref, "refs/"))
|
||||
ref += 5;
|
||||
if (!prefixcmp(ref, "notes/"))
|
||||
ref += 6;
|
||||
strbuf_addf(sb, "\nNotes (%s):\n", ref);
|
||||
}
|
||||
}
|
||||
|
||||
for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
|
||||
linelen = strchrnul(msg_p, '\n') - msg_p;
|
||||
|
@ -1030,3 +1178,31 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
|||
|
||||
free(msg);
|
||||
}
|
||||
|
||||
void format_display_notes(const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int flags)
|
||||
{
|
||||
int i;
|
||||
assert(display_notes_trees);
|
||||
for (i = 0; display_notes_trees[i]; i++)
|
||||
format_note(display_notes_trees[i], object_sha1, sb,
|
||||
output_encoding, flags);
|
||||
}
|
||||
|
||||
int copy_note(struct notes_tree *t,
|
||||
const unsigned char *from_obj, const unsigned char *to_obj,
|
||||
int force, combine_notes_fn combine_fn)
|
||||
{
|
||||
const unsigned char *note = get_note(t, from_obj);
|
||||
const unsigned char *existing_note = get_note(t, to_obj);
|
||||
|
||||
if (!force && existing_note)
|
||||
return 1;
|
||||
|
||||
if (note)
|
||||
add_note(t, to_obj, note, combine_fn);
|
||||
else if (existing_note)
|
||||
add_note(t, to_obj, null_sha1, combine_fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
65
notes.h
65
notes.h
|
@ -40,6 +40,7 @@ extern struct notes_tree {
|
|||
char *ref;
|
||||
combine_notes_fn *combine_notes;
|
||||
int initialized;
|
||||
int dirty;
|
||||
} default_notes_tree;
|
||||
|
||||
/*
|
||||
|
@ -99,6 +100,15 @@ void remove_note(struct notes_tree *t, const unsigned char *object_sha1);
|
|||
const unsigned char *get_note(struct notes_tree *t,
|
||||
const unsigned char *object_sha1);
|
||||
|
||||
/*
|
||||
* Copy a note from one object to another in the given notes_tree.
|
||||
*
|
||||
* Fails if the to_obj already has a note unless 'force' is true.
|
||||
*/
|
||||
int copy_note(struct notes_tree *t,
|
||||
const unsigned char *from_obj, const unsigned char *to_obj,
|
||||
int force, combine_notes_fn combine_fn);
|
||||
|
||||
/*
|
||||
* Flags controlling behaviour of for_each_note()
|
||||
*
|
||||
|
@ -198,4 +208,59 @@ void free_notes(struct notes_tree *t);
|
|||
void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int flags);
|
||||
|
||||
|
||||
struct string_list;
|
||||
|
||||
struct display_notes_opt {
|
||||
int suppress_default_notes:1;
|
||||
struct string_list *extra_notes_refs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Load the notes machinery for displaying several notes trees.
|
||||
*
|
||||
* If 'opt' is not NULL, then it specifies additional settings for the
|
||||
* displaying:
|
||||
*
|
||||
* - suppress_default_notes indicates that the notes from
|
||||
* core.notesRef and notes.displayRef should not be loaded.
|
||||
*
|
||||
* - extra_notes_refs may contain a list of globs (in the same style
|
||||
* as notes.displayRef) where notes should be loaded from.
|
||||
*/
|
||||
void init_display_notes(struct display_notes_opt *opt);
|
||||
|
||||
/*
|
||||
* Append notes for the given 'object_sha1' from all trees set up by
|
||||
* init_display_notes() to 'sb'. The 'flags' are a bitwise
|
||||
* combination of
|
||||
*
|
||||
* - NOTES_SHOW_HEADER: add a 'Notes (refname):' header
|
||||
*
|
||||
* - NOTES_INDENT: indent the notes by 4 places
|
||||
*
|
||||
* You *must* call init_display_notes() before using this function.
|
||||
*/
|
||||
void format_display_notes(const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int flags);
|
||||
|
||||
/*
|
||||
* Load the notes tree from each ref listed in 'refs'. The output is
|
||||
* an array of notes_tree*, terminated by a NULL.
|
||||
*/
|
||||
struct notes_tree **load_notes_trees(struct string_list *refs);
|
||||
|
||||
/*
|
||||
* Add all refs that match 'glob' to the 'list'.
|
||||
*/
|
||||
void string_list_add_refs_by_glob(struct string_list *list, const char *glob);
|
||||
|
||||
/*
|
||||
* Add all refs from a colon-separated glob list 'globs' to the end of
|
||||
* 'list'. Empty components are ignored. This helper is used to
|
||||
* parse GIT_NOTES_DISPLAY_REF style environment variables.
|
||||
*/
|
||||
void string_list_add_refs_from_colon_sep(struct string_list *list,
|
||||
const char *globs);
|
||||
|
||||
#endif
|
||||
|
|
6
pretty.c
6
pretty.c
|
@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
|
|||
}
|
||||
return 0; /* unknown %g placeholder */
|
||||
case 'N':
|
||||
format_note(NULL, commit->object.sha1, sb,
|
||||
format_display_notes(commit->object.sha1, sb,
|
||||
git_log_output_encoding ? git_log_output_encoding
|
||||
: git_commit_encoding, 0);
|
||||
return 1;
|
||||
|
@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
|
|||
strbuf_addch(sb, '\n');
|
||||
|
||||
if (context->show_notes)
|
||||
format_note(NULL, commit->object.sha1, sb, encoding,
|
||||
NOTES_SHOW_HEADER | NOTES_INDENT);
|
||||
format_display_notes(commit->object.sha1, sb, encoding,
|
||||
NOTES_SHOW_HEADER | NOTES_INDENT);
|
||||
|
||||
free(reencoded);
|
||||
}
|
||||
|
|
4
refs.c
4
refs.c
|
@ -698,7 +698,6 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
|
|||
{
|
||||
struct strbuf real_pattern = STRBUF_INIT;
|
||||
struct ref_filter filter;
|
||||
const char *has_glob_specials;
|
||||
int ret;
|
||||
|
||||
if (!prefix && prefixcmp(pattern, "refs/"))
|
||||
|
@ -707,8 +706,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
|
|||
strbuf_addstr(&real_pattern, prefix);
|
||||
strbuf_addstr(&real_pattern, pattern);
|
||||
|
||||
has_glob_specials = strpbrk(pattern, "?*[");
|
||||
if (!has_glob_specials) {
|
||||
if (!has_glob_specials(pattern)) {
|
||||
/* Append implied '/' '*' if not present. */
|
||||
if (real_pattern.buf[real_pattern.len - 1] != '/')
|
||||
strbuf_addch(&real_pattern, '/');
|
||||
|
|
5
refs.h
5
refs.h
|
@ -28,6 +28,11 @@ extern int for_each_replace_ref(each_ref_fn, void *);
|
|||
extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
|
||||
extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
|
||||
|
||||
static inline const char *has_glob_specials(const char *pattern)
|
||||
{
|
||||
return strpbrk(pattern, "?*[");
|
||||
}
|
||||
|
||||
/* can be used to learn about broken ref and symref */
|
||||
extern int for_each_rawref(each_ref_fn, void *);
|
||||
|
||||
|
|
21
revision.c
21
revision.c
|
@ -12,6 +12,7 @@
|
|||
#include "patch-ids.h"
|
||||
#include "decorate.h"
|
||||
#include "log-tree.h"
|
||||
#include "string-list.h"
|
||||
|
||||
volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
|
@ -1191,9 +1192,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
|
|||
} else if (!strcmp(arg, "--show-notes")) {
|
||||
revs->show_notes = 1;
|
||||
revs->show_notes_given = 1;
|
||||
} else if (!prefixcmp(arg, "--show-notes=")) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
revs->show_notes = 1;
|
||||
revs->show_notes_given = 1;
|
||||
if (!revs->notes_opt.extra_notes_refs)
|
||||
revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
|
||||
if (!prefixcmp(arg+13, "refs/"))
|
||||
/* happy */;
|
||||
else if (!prefixcmp(arg+13, "notes/"))
|
||||
strbuf_addstr(&buf, "refs/");
|
||||
else
|
||||
strbuf_addstr(&buf, "refs/notes/");
|
||||
strbuf_addstr(&buf, arg+13);
|
||||
string_list_append(strbuf_detach(&buf, NULL),
|
||||
revs->notes_opt.extra_notes_refs);
|
||||
} else if (!strcmp(arg, "--no-notes")) {
|
||||
revs->show_notes = 0;
|
||||
revs->show_notes_given = 1;
|
||||
} else if (!strcmp(arg, "--standard-notes")) {
|
||||
revs->show_notes_given = 1;
|
||||
revs->notes_opt.suppress_default_notes = 0;
|
||||
} else if (!strcmp(arg, "--no-standard-notes")) {
|
||||
revs->notes_opt.suppress_default_notes = 1;
|
||||
} else if (!strcmp(arg, "--oneline")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format("oneline", revs);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "parse-options.h"
|
||||
#include "grep.h"
|
||||
#include "notes.h"
|
||||
|
||||
#define SEEN (1u<<0)
|
||||
#define UNINTERESTING (1u<<1)
|
||||
|
@ -20,6 +21,7 @@
|
|||
|
||||
struct rev_info;
|
||||
struct log_info;
|
||||
struct string_list;
|
||||
|
||||
struct rev_info {
|
||||
/* Starting list */
|
||||
|
@ -126,6 +128,9 @@ struct rev_info {
|
|||
struct reflog_walk_info *reflog_info;
|
||||
struct decoration children;
|
||||
struct decoration merge_simplification;
|
||||
|
||||
/* notes-specific options: which refs to show */
|
||||
struct display_notes_opt notes_opt;
|
||||
};
|
||||
|
||||
#define REV_TREE_SAME 0
|
||||
|
|
377
t/t3301-notes.sh
377
t/t3301-notes.sh
|
@ -416,7 +416,7 @@ Date: Thu Apr 7 15:18:13 2005 -0700
|
|||
|
||||
6th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
other note
|
||||
EOF
|
||||
|
||||
|
@ -449,7 +449,139 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '
|
|||
test_cmp expect-not-other output
|
||||
'
|
||||
|
||||
cat > expect-both << EOF
|
||||
commit 387a89921c73d7ed72cd94d179c1c7048ca47756
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:18:13 2005 -0700
|
||||
|
||||
6th
|
||||
|
||||
Notes:
|
||||
order test
|
||||
|
||||
Notes (other):
|
||||
other note
|
||||
|
||||
commit bd1753200303d0a0344be813e504253b3d98e74d
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:17:13 2005 -0700
|
||||
|
||||
5th
|
||||
|
||||
Notes:
|
||||
replacement for deleted note
|
||||
EOF
|
||||
|
||||
test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
|
||||
GIT_NOTES_REF=refs/notes/commits git notes add \
|
||||
-m"replacement for deleted note" HEAD^ &&
|
||||
GIT_NOTES_REF=refs/notes/commits git notes add -m"order test" &&
|
||||
git config --unset core.notesRef &&
|
||||
git config notes.displayRef "refs/notes/*" &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
test_expect_success 'core.notesRef is implicitly in notes.displayRef' '
|
||||
git config core.notesRef refs/notes/commits &&
|
||||
git config notes.displayRef refs/notes/other &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
test_expect_success 'notes.displayRef can be given more than once' '
|
||||
git config --unset core.notesRef &&
|
||||
git config notes.displayRef refs/notes/commits &&
|
||||
git config --add notes.displayRef refs/notes/other &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
cat > expect-both-reversed << EOF
|
||||
commit 387a89921c73d7ed72cd94d179c1c7048ca47756
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:18:13 2005 -0700
|
||||
|
||||
6th
|
||||
|
||||
Notes (other):
|
||||
other note
|
||||
|
||||
Notes:
|
||||
order test
|
||||
EOF
|
||||
|
||||
test_expect_success 'notes.displayRef respects order' '
|
||||
git config core.notesRef refs/notes/other &&
|
||||
git config --unset-all notes.displayRef &&
|
||||
git config notes.displayRef refs/notes/commits &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect-both-reversed output
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
|
||||
git config --unset-all core.notesRef &&
|
||||
git config --unset-all notes.displayRef &&
|
||||
GIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \
|
||||
git log -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
cat > expect-none << EOF
|
||||
commit 387a89921c73d7ed72cd94d179c1c7048ca47756
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:18:13 2005 -0700
|
||||
|
||||
6th
|
||||
|
||||
commit bd1753200303d0a0344be813e504253b3d98e74d
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:17:13 2005 -0700
|
||||
|
||||
5th
|
||||
EOF
|
||||
|
||||
test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
|
||||
git config notes.displayRef "refs/notes/*" &&
|
||||
GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&
|
||||
test_cmp expect-none output
|
||||
'
|
||||
|
||||
test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
|
||||
GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
cat > expect-commits << EOF
|
||||
commit 387a89921c73d7ed72cd94d179c1c7048ca47756
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:18:13 2005 -0700
|
||||
|
||||
6th
|
||||
|
||||
Notes:
|
||||
order test
|
||||
EOF
|
||||
|
||||
test_expect_success '--no-standard-notes' '
|
||||
git log --no-standard-notes --show-notes=commits -1 > output &&
|
||||
test_cmp expect-commits output
|
||||
'
|
||||
|
||||
test_expect_success '--standard-notes' '
|
||||
git log --no-standard-notes --show-notes=commits \
|
||||
--standard-notes -2 > output &&
|
||||
test_cmp expect-both output
|
||||
'
|
||||
|
||||
test_expect_success '--show-notes=ref accumulates' '
|
||||
git log --show-notes=other --show-notes=commits \
|
||||
--no-standard-notes -1 > output &&
|
||||
test_cmp expect-both-reversed output
|
||||
'
|
||||
|
||||
test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
|
||||
git config core.notesRef refs/notes/other &&
|
||||
echo "Note on a tree" > expect
|
||||
git notes add -m "Note on a tree" HEAD: &&
|
||||
git notes show HEAD: > actual &&
|
||||
|
@ -473,7 +605,7 @@ Date: Thu Apr 7 15:19:13 2005 -0700
|
|||
|
||||
7th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
other note
|
||||
EOF
|
||||
|
||||
|
@ -504,7 +636,7 @@ Date: Thu Apr 7 15:21:13 2005 -0700
|
|||
|
||||
9th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
|
@ -534,7 +666,7 @@ Date: Thu Apr 7 15:21:13 2005 -0700
|
|||
|
||||
9th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
yet another note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
@ -553,7 +685,7 @@ Date: Thu Apr 7 15:22:13 2005 -0700
|
|||
|
||||
10th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
other note
|
||||
EOF
|
||||
|
||||
|
@ -570,7 +702,7 @@ Date: Thu Apr 7 15:22:13 2005 -0700
|
|||
|
||||
10th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
other note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
@ -589,7 +721,7 @@ Date: Thu Apr 7 15:23:13 2005 -0700
|
|||
|
||||
11th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
other note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
@ -620,7 +752,7 @@ Date: Thu Apr 7 15:23:13 2005 -0700
|
|||
|
||||
11th
|
||||
|
||||
Notes:
|
||||
Notes (other):
|
||||
yet another note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
@ -645,4 +777,233 @@ test_expect_success 'cannot copy note from object without notes' '
|
|||
test_must_fail git notes copy HEAD^ HEAD
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:25:13 2005 -0700
|
||||
|
||||
13th
|
||||
|
||||
Notes (other):
|
||||
yet another note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
||||
commit 7038787dfe22a14c3867ce816dbba39845359719
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:24:13 2005 -0700
|
||||
|
||||
12th
|
||||
|
||||
Notes (other):
|
||||
other note
|
||||
$whitespace
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --stdin' '
|
||||
(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
|
||||
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
|
||||
git notes copy --stdin &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect output &&
|
||||
test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
|
||||
test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:26:13 2005 -0700
|
||||
|
||||
14th
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
|
||||
test_commit 14th &&
|
||||
test_commit 15th &&
|
||||
(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
|
||||
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
yet another note
|
||||
$whitespace
|
||||
yet another note
|
||||
|
||||
commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:26:13 2005 -0700
|
||||
|
||||
14th
|
||||
|
||||
Notes (other):
|
||||
other note
|
||||
$whitespace
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (enabled)' '
|
||||
git config notes.rewriteMode overwrite &&
|
||||
git config notes.rewriteRef "refs/notes/*" &&
|
||||
(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
|
||||
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (disabled)' '
|
||||
git config notes.rewrite.bar false &&
|
||||
echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
|
||||
git notes copy --for-rewrite=bar &&
|
||||
git log -2 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
a fresh note
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (overwrite)' '
|
||||
git notes add -f -m"a fresh note" HEAD^ &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (ignore)' '
|
||||
git config notes.rewriteMode ignore &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
a fresh note
|
||||
another fresh note
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (append)' '
|
||||
git notes add -f -m"another fresh note" HEAD^ &&
|
||||
git config notes.rewriteMode concatenate &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
a fresh note
|
||||
another fresh note
|
||||
append 1
|
||||
append 2
|
||||
EOF
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (append two to one)' '
|
||||
git notes add -f -m"append 1" HEAD^ &&
|
||||
git notes add -f -m"append 2" HEAD^^ &&
|
||||
(echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
|
||||
echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
test_expect_success 'git notes copy --for-rewrite (append empty)' '
|
||||
git notes remove HEAD^ &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
replacement note 1
|
||||
EOF
|
||||
|
||||
test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
|
||||
git notes add -f -m"replacement note 1" HEAD^ &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:27:13 2005 -0700
|
||||
|
||||
15th
|
||||
|
||||
Notes (other):
|
||||
replacement note 2
|
||||
EOF
|
||||
|
||||
test_expect_success 'GIT_NOTES_REWRITE_REF works' '
|
||||
git config notes.rewriteMode overwrite &&
|
||||
git notes add -f -m"replacement note 2" HEAD^ &&
|
||||
git config --unset-all notes.rewriteRef &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
|
||||
git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
|
||||
git config notes.rewriteRef refs/notes/other &&
|
||||
git notes add -f -m"replacement note 3" HEAD^ &&
|
||||
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
|
||||
GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
test_done
|
||||
|
|
|
@ -151,4 +151,21 @@ test_expect_success 'Rebase a commit that sprinkles CRs in' '
|
|||
git diff --exit-code file-with-cr:CR HEAD:CR
|
||||
'
|
||||
|
||||
test_expect_success 'rebase can copy notes' '
|
||||
git config notes.rewrite.rebase true &&
|
||||
git config notes.rewriteRef "refs/notes/*" &&
|
||||
test_commit n1 &&
|
||||
test_commit n2 &&
|
||||
test_commit n3 &&
|
||||
git notes add -m"a note" n3 &&
|
||||
git rebase --onto n1 n2 &&
|
||||
test "a note" = "$(git notes show HEAD)"
|
||||
'
|
||||
|
||||
test_expect_success 'rebase -m can copy notes' '
|
||||
git reset --hard n3 &&
|
||||
git rebase -m --onto n1 n2 &&
|
||||
test "a note" = "$(git notes show HEAD)"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -553,4 +553,28 @@ test_expect_success 'reword' '
|
|||
git show HEAD~2 | grep "C changed"
|
||||
'
|
||||
|
||||
test_expect_success 'rebase -i can copy notes' '
|
||||
git config notes.rewrite.rebase true &&
|
||||
git config notes.rewriteRef "refs/notes/*" &&
|
||||
test_commit n1 &&
|
||||
test_commit n2 &&
|
||||
test_commit n3 &&
|
||||
git notes add -m"a note" n3 &&
|
||||
git rebase --onto n1 n2 &&
|
||||
test "a note" = "$(git notes show HEAD)"
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
an earlier note
|
||||
a note
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i can copy notes over a fixup' '
|
||||
git reset --hard n3 &&
|
||||
git notes add -m"an earlier note" n2 &&
|
||||
GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
|
||||
git notes show > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2010 Thomas Rast
|
||||
#
|
||||
|
||||
test_description='Test the post-rewrite hook.'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_commit A foo A &&
|
||||
test_commit B foo B &&
|
||||
test_commit C foo C &&
|
||||
test_commit D foo D
|
||||
'
|
||||
|
||||
mkdir .git/hooks
|
||||
|
||||
cat >.git/hooks/post-rewrite <<EOF
|
||||
#!/bin/sh
|
||||
echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
|
||||
cat > "$TRASH_DIRECTORY"/post-rewrite.data
|
||||
EOF
|
||||
chmod u+x .git/hooks/post-rewrite
|
||||
|
||||
clear_hook_input () {
|
||||
rm -f post-rewrite.args post-rewrite.data
|
||||
}
|
||||
|
||||
verify_hook_input () {
|
||||
test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
|
||||
test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
|
||||
}
|
||||
|
||||
test_expect_success 'git commit --amend' '
|
||||
clear_hook_input &&
|
||||
echo "D new message" > newmsg &&
|
||||
oldsha=$(git rev-parse HEAD^0) &&
|
||||
git commit -Fnewmsg --amend &&
|
||||
echo amend > expected.args &&
|
||||
echo $oldsha $(git rev-parse HEAD^0) > expected.data &&
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git commit --amend --no-post-rewrite' '
|
||||
clear_hook_input &&
|
||||
echo "D new message again" > newmsg &&
|
||||
git commit --no-post-rewrite -Fnewmsg --amend &&
|
||||
test ! -f post-rewrite.args &&
|
||||
test ! -f post-rewrite.data
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto A B &&
|
||||
echo C > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse C) $(git rev-parse HEAD^)
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase --skip' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto A B &&
|
||||
test_must_fail git rebase --skip &&
|
||||
echo D > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase -m' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase -m --onto A B &&
|
||||
echo C > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse C) $(git rev-parse HEAD^)
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase -m --skip' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto A B &&
|
||||
test_must_fail git rebase --skip &&
|
||||
echo D > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
. "$TEST_DIRECTORY"/lib-rebase.sh
|
||||
|
||||
set_fake_editor
|
||||
|
||||
# Helper to work around the lack of one-shot exporting for
|
||||
# test_must_fail (as it is a shell function)
|
||||
test_fail_interactive_rebase () {
|
||||
(
|
||||
FAKE_LINES="$1" &&
|
||||
shift &&
|
||||
export FAKE_LINES &&
|
||||
test_must_fail git rebase -i "$@"
|
||||
)
|
||||
}
|
||||
|
||||
test_expect_success 'git rebase -i (unchanged)' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_fail_interactive_rebase "1 2" --onto A B &&
|
||||
echo C > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse C) $(git rev-parse HEAD^)
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase -i (skip)' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_fail_interactive_rebase "2" --onto A B &&
|
||||
echo D > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase -i (squash)' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_fail_interactive_rebase "1 squash 2" --onto A B &&
|
||||
echo C > foo &&
|
||||
git add foo &&
|
||||
git rebase --continue &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse C) $(git rev-parse HEAD)
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase -i (fixup without conflict)' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
FAKE_LINES="1 fixup 2" git rebase -i B &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<EOF &&
|
||||
$(git rev-parse C) $(git rev-parse HEAD)
|
||||
$(git rev-parse D) $(git rev-parse HEAD)
|
||||
EOF
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_done
|
|
@ -425,4 +425,16 @@ test_expect_success 'amend using the message from a commit named with tag' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'amend can copy notes' '
|
||||
|
||||
git config notes.rewrite.amend true &&
|
||||
git config notes.rewriteRef "refs/notes/*" &&
|
||||
test_commit foo &&
|
||||
git notes add -m"a note" &&
|
||||
test_tick &&
|
||||
git commit --amend -m"new foo" &&
|
||||
test "$(git notes show)" = "a note"
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -54,6 +54,10 @@ unset GIT_OBJECT_DIRECTORY
|
|||
unset GIT_CEILING_DIRECTORIES
|
||||
unset SHA1_FILE_DIRECTORIES
|
||||
unset SHA1_FILE_DIRECTORY
|
||||
unset GIT_NOTES_REF
|
||||
unset GIT_NOTES_DISPLAY_REF
|
||||
unset GIT_NOTES_REWRITE_REF
|
||||
unset GIT_NOTES_REWRITE_MODE
|
||||
GIT_MERGE_VERBOSITY=5
|
||||
export GIT_MERGE_VERBOSITY
|
||||
export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
|
||||
|
|
Загрузка…
Ссылка в новой задаче