pushUpdateRejected::
Set this variable to 'false' if you want to disable
'pushNonFFCurrent', 'pushNonFFDefault',
- 'pushNonFFMatching', and 'pushAlreadyExists'
+ 'pushNonFFMatching', 'pushAlreadyExists',
+ 'pushFetchFirst', and 'pushNeedsForce'
simultaneously.
pushNonFFCurrent::
Advice shown when linkgit:git-push[1] fails due to a
pushAlreadyExists::
Shown when linkgit:git-push[1] rejects an update that
does not qualify for fast-forwarding (e.g., a tag.)
+ pushFetchFirst::
+ Shown when linkgit:git-push[1] rejects an update that
+ tries to overwrite a remote ref that points at an
+ object we do not have.
+ pushNeedsForce::
+ Shown when linkgit:git-push[1] rejects an update that
+ tries to overwrite a remote ref that points at an
+ object that is not a committish, or make the remote
+ ref point at an object that is not a committish.
statusHints::
Show directions on how to proceed from the current
- state in the output of linkgit:git-status[1] and in
+ state in the output of linkgit:git-status[1], in
the template shown when writing commit messages in
- linkgit:git-commit[1].
+ linkgit:git-commit[1], and in the help message shown
+ by linkgit:git-checkout[1] when switching branch.
commitBeforeMerge::
Advice shown when linkgit:git-merge[1] refuses to
merge to avoid overwriting local changes.
crawlers and some backup systems).
See linkgit:git-update-index[1]. True by default.
+core.checkstat::
+ Determines which stat fields to match between the index
+ and work tree. The user can set this to 'default' or
+ 'minimal'. Default (or explicitly 'default'), is to check
+ all fields, including the sub-second part of mtime and ctime.
+
core.quotepath::
The commands that output paths (e.g. 'ls-files',
'diff'), when not given the `-z` option, will quote
variable when it is set, and the environment variable
`GIT_EDITOR` is not set. See linkgit:git-var[1].
+core.commentchar::
+ Commands such as `commit` and `tag` that lets you edit
+ messages consider a line that begins with this character
+ commented, and removes them after the editor returns
+ (default '#').
+
sequence.editor::
Text editor used by `git rebase -i` for editing the rebase insn file.
The value is meant to be interpreted by the shell when it is used.
it unless you understand the implications (see linkgit:git-rebase[1]
for details).
+branch.<name>.description::
+ Branch description, can be edited with
+ `git branch --edit-description`. Branch description is
+ automatically added in the format-patch cover letter or
+ request-pull summary.
+
browser.<tool>.cmd::
Specify the command to invoke the specified browser. The
specified command is evaluated in shell with the URLs passed
Specify whether to output tag listing in `git tag` in columns.
See `column.ui` for details.
+commit.cleanup::
+ This setting overrides the default of the `--cleanup` option in
+ `git commit`. See linkgit:git-commit[1] for details. Changing the
+ default can be useful when you always want to keep lines that begin
+ with comment character `#` in your log message, in which case you
+ would do `git config commit.cleanup whitespace` (note that you will
+ have to remove the help lines that begin with `#` in the commit log
+ template yourself, if you do this).
+
commit.status::
A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit
difftool.prompt::
Prompt before each invocation of the diff tool.
-diff.wordRegex::
- A POSIX Extended Regular Expression used to determine what is a "word"
- when performing word-by-word difference calculations. Character
- sequences that match the regular expression are "words", all other
- characters are *ignorable* whitespace.
-
fetch.recurseSubmodules::
This option can be either set to a boolean value or to 'on-demand'.
Setting it to a boolean changes the behavior of fetch and pull to
value is 0 - the command will be just shown but not executed.
This is the default.
+help.htmlpath::
+ Specify the path where the HTML documentation resides. File system paths
+ and URLs are supported. HTML pages will be prefixed with this path when
+ help is displayed in the 'web' format. This defaults to the documentation
+ path of your Git installation.
+
http.proxy::
Override the HTTP proxy, normally configured using the 'http_proxy',
'https_proxy', and 'all_proxy' environment variables (see
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
normally hide the root commit will now show it. True by default.
+log.mailmap::
+ If true, makes linkgit:git-log[1], linkgit:git-show[1], and
+ linkgit:git-whatchanged[1] assume `--use-mailmap`.
+
mailmap.file::
The location of an augmenting mailmap file. The default
mailmap, located in the root of the repository, is loaded
subdirectory, or somewhere outside of the repository itself.
See linkgit:git-shortlog[1] and linkgit:git-blame[1].
+mailmap.blob::
+ Like `mailmap.file`, but consider the value as a reference to a
+ blob in the repository. If both `mailmap.file` and
+ `mailmap.blob` are given, both are parsed, with entries from
+ `mailmap.file` taking precedence. In a bare repository, this
+ defaults to `HEAD:.mailmap`. In a non-bare repository, it
+ defaults to empty.
+
man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].
URL and other values found in the `.gitmodules` file. See
linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+submodule.<name>.branch::
+ The remote branch name for a submodule, used by `git submodule
+ update --remote`. Set this option to override the value found in
+ the `.gitmodules` file. See linkgit:git-submodule[1] and
+ linkgit:gitmodules[5] for details.
+
submodule.<name>.fetchRecurseSubmodules::
This option can be used to control recursive fetching of this
submodule. It can be overridden by using the --[no-]recurse-submodules
"(e.g. 'git pull') before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
+ static const char message_advice_ref_fetch_first[] =
+ N_("Updates were rejected because the remote contains work that you do\n"
+ "not have locally. This is usually caused by another repository pushing\n"
+ "to the same ref. You may want to first merge the remote changes (e.g.,\n"
+ "'git pull') before pushing again.\n"
+ "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
static const char message_advice_ref_already_exists[] =
- N_("Updates were rejected because the destination reference already exists\n"
- "in the remote.");
+ N_("Updates were rejected because the tag already exists in the remote.");
+
+ static const char message_advice_ref_needs_force[] =
+ N_("You cannot update a remote ref that points at a non-commit object,\n"
+ "or update a remote ref to make it point at a non-commit object,\n"
+ "without using the '--force' option.\n");
static void advise_pull_before_push(void)
{
advise(_(message_advice_ref_already_exists));
}
+ static void advise_ref_fetch_first(void)
+ {
+ if (!advice_push_fetch_first || !advice_push_update_rejected)
+ return;
+ advise(_(message_advice_ref_fetch_first));
+ }
+
+ static void advise_ref_needs_force(void)
+ {
+ if (!advice_push_needs_force || !advice_push_update_rejected)
+ return;
+ advise(_(message_advice_ref_needs_force));
+ }
+
static int push_with_options(struct transport *transport, int flags)
{
int err;
advise_checkout_pull_push();
} else if (reject_reasons & REJECT_ALREADY_EXISTS) {
advise_ref_already_exists();
+ } else if (reject_reasons & REJECT_FETCH_FIRST) {
+ advise_ref_fetch_first();
+ } else if (reject_reasons & REJECT_NEEDS_FORCE) {
+ advise_ref_needs_force();
}
return 1;
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
OPT_BIT(0, "prune", &flags, N_("prune locally removed refs"),
TRANSPORT_PUSH_PRUNE),
+ OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
OPT_END()
};
#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"
+#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
/*
* Repository-local GIT_* environment variables
extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */
+
struct pathspec {
const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
int nr;
struct pathspec_item {
const char *match;
int len;
- unsigned int use_wildcard:1;
+ int nowildcard_len;
+ int flags;
} *items;
};
extern void free_pathspec(struct pathspec *);
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
+extern int limit_pathspec_to_literal(void);
+
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
extern int trust_ctime;
+extern int check_stat;
extern int quote_path_fully;
extern int has_symlinks;
extern int minimum_abbrev, default_abbrev;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
+/*
+ * The character that begins a commented line in user-editable file
+ * that is subject to stripspace.
+ */
+extern char comment_line_char;
+
enum branch_track {
BRANCH_TRACK_UNSPECIFIED = -1,
BRANCH_TRACK_NEVER = 0,
}
int is_directory(const char *);
const char *real_path(const char *path);
+const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path);
const char *relative_path(const char *abs, const char *base);
int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, const char *prefix_list);
+int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
int daemon_avoid_alias(const char *path);
int offset_1st_component(const char *path);
char *symref;
unsigned int
force:1,
- requires_force:1,
+ forced_update:1,
merge:1,
- nonfastforward:1,
- update:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
REF_STATUS_REJECT_NONFASTFORWARD,
REF_STATUS_REJECT_ALREADY_EXISTS,
REF_STATUS_REJECT_NODELETE,
+ REF_STATUS_REJECT_FETCH_FIRST,
+ REF_STATUS_REJECT_NEEDS_FORCE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
REF_STATUS_EXPECTING_REPORT
extern int git_env_bool(const char *, int);
extern int git_config_system(void);
extern int config_error_nonbool(const char *);
+#if defined(__GNUC__) && ! defined(__clang__)
+#define config_error_nonbool(s) (config_error_nonbool(s), -1)
+#endif
extern const char *get_log_output_encoding(void);
extern const char *get_commit_output_encoding(void);
#define CONFIG_INCLUDE_INIT { 0 }
extern int git_config_include(const char *name, const char *value, void *data);
-#define IDENT_NAME_GIVEN 01
-#define IDENT_MAIL_GIVEN 02
-#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
-extern int user_ident_explicitly_given;
-extern int user_ident_sufficiently_given(void);
+/*
+ * Match and parse a config key of the form:
+ *
+ * section.(subsection.)?key
+ *
+ * (i.e., what gets handed to a config_fn_t). The caller provides the section;
+ * we return -1 if it does not match, 0 otherwise. The subsection and key
+ * out-parameters are filled by the function (and subsection is NULL if it is
+ * missing).
+ */
+extern int parse_config_key(const char *var,
+ const char *section,
+ const char **subsection, int *subsection_len,
+ const char **key);
+
+extern int committer_ident_sufficiently_given(void);
+extern int author_ident_sufficiently_given(void);
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern const char *git_mailmap_file;
+extern const char *git_mailmap_blob;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
* passing the --force argument
*/
- ref->update =
- !ref->deletion &&
- !is_null_sha1(ref->old_sha1);
-
- if (ref->update) {
- ref->nonfastforward =
- !has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1);
-
- if (!prefixcmp(ref->name, "refs/tags/")) {
- ref->requires_force = 1;
- if (!force_ref_update) {
- ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
- continue;
- }
- } else if (ref->nonfastforward) {
- ref->requires_force = 1;
- if (!force_ref_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
- }
- }
+ if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
+ int why = 0; /* why would this push require --force? */
+
+ if (!prefixcmp(ref->name, "refs/tags/"))
+ why = REF_STATUS_REJECT_ALREADY_EXISTS;
+ else if (!has_sha1_file(ref->old_sha1))
+ why = REF_STATUS_REJECT_FETCH_FIRST;
+ else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
+ !lookup_commit_reference_gently(ref->new_sha1, 1))
+ why = REF_STATUS_REJECT_NEEDS_FORCE;
+ else if (!ref_newer(ref->new_sha1, ref->old_sha1))
+ why = REF_STATUS_REJECT_NONFASTFORWARD;
+
+ if (!force_ref_update)
+ ref->status = why;
+ else if (why)
+ ref->forced_update = 1;
}
}
}
return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
}
+static int ignore_symref_update(const char *refname)
+{
+ unsigned char sha1[20];
+ int flag;
+
+ if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
+ return 0; /* non-existing refs are OK */
+ return (flag & REF_ISSYMREF);
+}
+
static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec *refspec)
{
if (strchr(ref->name, '^'))
continue; /* a dereference item */
if (match_name_with_pattern(refspec->src, ref->name,
- refspec->dst, &expn_name)) {
+ refspec->dst, &expn_name) &&
+ !ignore_symref_update(expn_name)) {
struct ref *cpy = copy_ref(ref);
cpy->peer_ref = alloc_ref(expn_name);
for (rmp = &ref_map; *rmp; ) {
if ((*rmp)->peer_ref) {
- if (check_refname_format((*rmp)->peer_ref->name + 5,
- REFNAME_ALLOW_ONELEVEL)) {
+ if (prefixcmp((*rmp)->peer_ref->name, "refs/") ||
+ check_refname_format((*rmp)->peer_ref->name, 0)) {
struct ref *ignore = *rmp;
error("* Ignoring funny ref '%s' locally",
(*rmp)->peer_ref->name);
struct commit_list *list, *used;
int found = 0;
- /* Both new and old must be commit-ish and new is descendant of
+ /*
+ * Both new and old must be commit-ish and new is descendant of
* old. Otherwise we require --force.
*/
o = deref_tag(parse_object(old_sha1), NULL, 0);
base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0);
- if (!num_theirs)
+ if (!num_theirs) {
strbuf_addf(sb,
Q_("Your branch is ahead of '%s' by %d commit.\n",
"Your branch is ahead of '%s' by %d commits.\n",
num_ours),
base, num_ours);
- else if (!num_ours)
+ if (advice_status_hints)
+ strbuf_addf(sb,
+ _(" (use \"git push\" to publish your local commits)\n"));
+ } else if (!num_ours) {
strbuf_addf(sb,
Q_("Your branch is behind '%s' by %d commit, "
"and can be fast-forwarded.\n",
"and can be fast-forwarded.\n",
num_theirs),
base, num_theirs);
- else
+ if (advice_status_hints)
+ strbuf_addf(sb,
+ _(" (use \"git pull\" to update your local branch)\n"));
+ } else {
strbuf_addf(sb,
Q_("Your branch and '%s' have diverged,\n"
"and have %d and %d different commit each, "
"respectively.\n",
num_theirs),
base, num_ours, num_theirs);
+ if (advice_status_hints)
+ strbuf_addf(sb,
+ _(" (use \"git pull\" to merge the remote branch into yours)\n"));
+ }
return 1;
}
const char *msg;
strcpy(quickref, status_abbrev(ref->old_sha1));
- if (ref->requires_force) {
+ if (ref->forced_update) {
strcat(quickref, "...");
type = '+';
msg = "forced update";
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"already exists", porcelain);
break;
+ case REF_STATUS_REJECT_FETCH_FIRST:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "fetch first", porcelain);
+ break;
+ case REF_STATUS_REJECT_NEEDS_FORCE:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "needs force", porcelain);
+ break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
*reject_reasons |= REJECT_NON_FF_OTHER;
} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
*reject_reasons |= REJECT_ALREADY_EXISTS;
+ } else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) {
+ *reject_reasons |= REJECT_FETCH_FIRST;
+ } else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {
+ *reject_reasons |= REJECT_NEEDS_FORCE;
}
}
}
die("Aborting.");
}
+static int run_pre_push_hook(struct transport *transport,
+ struct ref *remote_refs)
+{
+ int ret = 0, x;
+ struct ref *r;
+ struct child_process proc;
+ struct strbuf buf;
+ const char *argv[4];
+
+ if (!(argv[0] = find_hook("pre-push")))
+ return 0;
+
+ argv[1] = transport->remote->name;
+ argv[2] = transport->url;
+ argv[3] = NULL;
+
+ memset(&proc, 0, sizeof(proc));
+ proc.argv = argv;
+ proc.in = -1;
+
+ if (start_command(&proc)) {
+ finish_command(&proc);
+ return -1;
+ }
+
+ strbuf_init(&buf, 256);
+
+ for (r = remote_refs; r; r = r->next) {
+ if (!r->peer_ref) continue;
+ if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
+ if (r->status == REF_STATUS_UPTODATE) continue;
+
+ strbuf_reset(&buf);
+ strbuf_addf( &buf, "%s %s %s %s\n",
+ r->peer_ref->name, sha1_to_hex(r->new_sha1),
+ r->name, sha1_to_hex(r->old_sha1));
+
+ if (write_in_full(proc.in, buf.buf, buf.len) != buf.len) {
+ ret = -1;
+ break;
+ }
+ }
+
+ strbuf_release(&buf);
+
+ x = close(proc.in);
+ if (!ret)
+ ret = x;
+
+ x = finish_command(&proc);
+ if (!ret)
+ ret = x;
+
+ return ret;
+}
+
int transport_push(struct transport *transport,
int refspec_nr, const char **refspec, int flags,
unsigned int *reject_reasons)
flags & TRANSPORT_PUSH_MIRROR,
flags & TRANSPORT_PUSH_FORCE);
+ if (!(flags & TRANSPORT_PUSH_NO_HOOK))
+ if (run_pre_push_hook(transport, remote_refs))
+ return -1;
+
if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
struct ref *ref = remote_refs;
for (; ref; ref = ref->next)
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
#define TRANSPORT_PUSH_PRUNE 128
#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
+#define TRANSPORT_PUSH_NO_HOOK 512
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
#define REJECT_NON_FF_HEAD 0x01
#define REJECT_NON_FF_OTHER 0x02
#define REJECT_ALREADY_EXISTS 0x04
+ #define REJECT_FETCH_FIRST 0x08
+ #define REJECT_NEEDS_FORCE 0x10
int transport_push(struct transport *connection,
int refspec_nr, const char **refspec, int flags,