Require "-f" for push to update a tag, even if it is a fast-forward.
* cr/push-force-tag-update:
push: allow already-exists advice to be disabled
push: rename config variable for more general use
push: cleanup push rules comment
push: clarify rejection of update to non-commit-ish
push: require force for annotated tags
push: require force for refs under refs/tags/
push: flag updates that require force
push: keep track of "update" state separately
push: add advice for rejected tag reference
push: return reject reasons as a bitset
can tell Git that you do not need help by setting these to 'false':
+
--
- pushNonFastForward::
+ pushUpdateRejected::
Set this variable to 'false' if you want to disable
- 'pushNonFFCurrent', 'pushNonFFDefault', and
- 'pushNonFFMatching' simultaneously.
+ 'pushNonFFCurrent', 'pushNonFFDefault',
+ 'pushNonFFMatching', and 'pushAlreadyExists'
+ simultaneously.
pushNonFFCurrent::
Advice shown when linkgit:git-push[1] fails due to a
non-fast-forward update to the current branch.
'matching refs' explicitly (i.e. you used ':', or
specified a refspec that isn't your current branch) and
it resulted in a non-fast-forward error.
+ pushAlreadyExists::
+ Shown when linkgit:git-push[1] rejects an update that
+ does not qualify for fast-forwarding (e.g., a tag.)
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.
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
updated.
+
The object referenced by <src> is used to update the <dst> reference
- on the remote side, but by default this is only allowed if the
- update can fast-forward <dst>. By having the optional leading `+`,
- you can tell git to update the <dst> ref even when the update is not a
- fast-forward. This does *not* attempt to merge <src> into <dst>. See
+ on the remote side. By default this is only allowed if <dst> is not
+ a tag (annotated or lightweight), and then only if it can fast-forward
+ <dst>. By having the optional leading `+`, you can tell git to update
+ the <dst> ref even if it is not allowed by default (e.g., it is not a
+ fast-forward.) This does *not* attempt to merge <src> into <dst>. See
EXAMPLES below for details.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
----------------
Further suppose that the other person already pushed changes leading to A
-back to the original repository you two obtained the original commit X.
+back to the original repository from which you two obtained the original
+commit X.
The push done by the other person updated the branch that used to point at
commit X to point at commit A. It is a fast-forward.
A handy way to push the current branch to the same name on the
remote.
-`git push origin master:satellite/master dev:satellite/dev`::
+`git push mothership master:satellite/master dev:satellite/dev`::
Use the source ref that matches `master` (e.g. `refs/heads/master`)
to update the ref that matches `satellite/master` (most probably
- `refs/remotes/satellite/master`) in the `origin` repository, then
+ `refs/remotes/satellite/master`) in the `mothership` repository;
do the same for `dev` and `satellite/dev`.
++
+This is to emulate `git fetch` run on the `mothership` using `git
+push` that is run in the opposite direction in order to integrate
+the work done on `satellite`, and is often necessary when you can
+only make connection in one way (i.e. satellite can ssh into
+mothership but mothership cannot initiate connection to satellite
+because the latter is behind a firewall or does not run sshd).
++
+After running this `git push` on the `satellite` machine, you would
+ssh into the `mothership` and run `git merge` there to complete the
+emulation of `git pull` that were run on `mothership` to pull changes
+made on `satellite`.
`git push origin HEAD:master`::
Push the current branch to the remote ref matching `master` in the
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;
};
}
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);
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char *symref;
- unsigned int force:1,
+ unsigned int
+ force:1,
+ requires_force:1,
merge:1,
nonfastforward:1,
+ not_forwardable:1,
+ update:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
REF_STATUS_REJECT_NONFASTFORWARD,
+ REF_STATUS_REJECT_ALREADY_EXISTS,
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
#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);
+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;
return 0;
}
+ static inline int is_forwardable(struct ref* ref)
+ {
+ struct object *o;
+
+ if (!prefixcmp(ref->name, "refs/tags/"))
+ return 0;
+
+ /* old object must be a commit */
+ o = parse_object(ref->old_sha1);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+
+ /* new object must be commit-ish */
+ o = deref_tag(parse_object(ref->new_sha1), NULL, 0);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+
+ return 1;
+ }
+
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
{
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
+ int force_ref_update = ref->force || force_update;
+
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!send_mirror)
continue;
}
- /* This part determines what can overwrite what.
- * The rules are:
+ /*
+ * The below logic determines whether an individual
+ * refspec A:B can be pushed. The push will succeed
+ * if any of the following are true:
+ *
+ * (1) the remote reference B does not exist
*
- * (0) you can always use --force or +A:B notation to
- * selectively force individual ref pairs.
+ * (2) the remote reference B is being removed (i.e.,
+ * pushing :B where no source is specified)
*
- * (1) if the old thing does not exist, it is OK.
+ * (3) the update meets all fast-forwarding criteria:
*
- * (2) if you do not have the old thing, you are not allowed
- * to overwrite it; you would not know what you are losing
- * otherwise.
+ * (a) the destination is not under refs/tags/
+ * (b) the old is a commit
+ * (c) the new is a descendant of the old
*
- * (3) if both new and old are commit-ish, and new is a
- * descendant of old, it is OK.
+ * NOTE: We must actually have the old object in
+ * order to overwrite it in the remote reference,
+ * and the new object must be commit-ish. These are
+ * implied by (b) and (c) respectively.
*
- * (4) regardless of all of the above, removing :B is
- * always allowed.
+ * (4) it is forced using the +A:B notation, or by
+ * passing the --force argument
*/
- ref->nonfastforward =
- !ref->deletion &&
- !is_null_sha1(ref->old_sha1) &&
- (!has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1));
+ ref->not_forwardable = !is_forwardable(ref);
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ 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 (ref->not_forwardable) {
+ 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;
+ }
+ }
}
}
}
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);
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;
}