* [PATCH 3/3] mailinfo: with -b, keep space after [foo]
From: Thomas Rast @ 2012-01-11 20:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <e915a551c9bbf12f4d8fd929e9ed24f3223790ee.1326312730.git.trast@student.ethz.ch>
The logic for the -b mode, where [PATCH] is dropped but [foo] is not,
silently ate all spaces after the ].
Fix this by keeping the next isspace() character, if there is any.
Being more thorough is pointless, as the later cleanup_space() call
will normalize any sequence of whitespace to a single ' '.
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
builtin/mailinfo.c | 11 ++++++++++-
t/t4150-am.sh | 2 +-
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index bfb32b7..eaf9e15 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -250,8 +250,17 @@ static void cleanup_subject(struct strbuf *subject)
(7 <= remove &&
memmem(subject->buf + at, remove, "PATCH", 5)))
strbuf_remove(subject, at, remove);
- else
+ else {
at += remove;
+ /*
+ * If the input had a space after the ], keep
+ * it. We don't bother with finding the end of
+ * the space, since we later normalize it
+ * anyway.
+ */
+ if (isspace(subject->buf[at]))
+ at += 1;
+ }
continue;
}
break;
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 7e7c83c..8807b60 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -262,7 +262,7 @@ test_expect_success 'am --keep really keeps the subject' '
grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
'
-test_expect_failure 'am --keep-non-patch really keeps the non-patch part' '
+test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout HEAD^ &&
--
1.7.9.rc0.168.g3847c
^ permalink raw reply related
* [PATCH 1/3] mailinfo documentation: accurately describe non -k case
From: Thomas Rast @ 2012-01-11 20:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
Since its very first description of -k, the documentation for
git-mailinfo claimed that (in the case without -k) after cleaning up
bracketed strings [blah], it would insert [PATCH].
It doesn't; on the contrary, one of the important jobs of mailinfo is
to remove those strings.
Since we're already there, rewrite the paragraph to give a complete
enumeration of all the transformations. Specifically, it was missing
the whitespace normalization (run of isspace(c) -> ' ') and the
removal of leading ':'.
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
Documentation/git-mailinfo.txt | 25 ++++++++++++++++++-------
1 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 51dc325..97e7a8e 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -25,13 +25,24 @@ command directly. See linkgit:git-am[1] instead.
OPTIONS
-------
-k::
- Usually the program 'cleans up' the Subject: header line
- to extract the title line for the commit log message,
- among which (1) remove 'Re:' or 're:', (2) leading
- whitespaces, (3) '[' up to ']', typically '[PATCH]', and
- then prepends "[PATCH] ". This flag forbids this
- munging, and is most useful when used to read back
- 'git format-patch -k' output.
+ Usually the program removes email cruft from the Subject:
+ header line to extract the title line for the commit log
+ message. This option prevents this munging, and is most
+ useful when used to read back 'git format-patch -k' output.
++
+Specifically, the following are removed until none of them remain:
++
+--
+* Leading and trailing whitespace.
+
+* Leading `Re:`, `re:`, and `:`.
+
+* Leading bracketed strings (between `[` and `]`, usually
+ `[PATCH]`).
+--
++
+Finally, runs of whitespace are normalized to a single ASCII space
+character.
-b::
When -k is not in effect, all leading strings bracketed with '['
--
1.7.9.rc0.168.g3847c
^ permalink raw reply related
* Re: [PATCH 2/2] diff --word-diff: use non-whitespace regex by default
From: Thomas Rast @ 2012-01-11 20:05 UTC (permalink / raw)
To: Tay Ray Chuan; +Cc: Git Mailing List, Junio C Hamano
In-Reply-To: <1326302702-4536-2-git-send-email-rctay89@gmail.com>
Tay Ray Chuan <rctay89@gmail.com> writes:
> Factor out the comprehensive non-whitespace regex in use by PATTERNS and
> IPATTERN and use it as the word-diff regex for the default diff driver.
Why?
I seem to recall that the motivation for keeping the original code as-is
instead of just emulating its behavior with a default regex was that it
is faster. So disabling the default mode should at least have an
advantage?
</devils-advocate>
--
Thomas Rast
trast@{inf,student}.ethz.ch
^ permalink raw reply
* Re: [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>
From: Junio C Hamano @ 2012-01-11 20:01 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King
In-Reply-To: <1326189427-20800-8-git-send-email-pclouds@gmail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> It does not make sense to look outside refs/heads for HEAD's target
> (src_ref_prefix can be set to "refs/" if --mirror is used) because ref
> code only allows symref in form refs/heads/...
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> builtin/clone.c | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/clone.c b/builtin/clone.c
> index 05224d7..960242f 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,
> static void update_head(const struct ref *our, const struct ref *remote,
> const char *msg)
> {
> - if (our) {
> + if (our && !prefixcmp(our->name, "refs/heads/")) {
> /* Local default branch link */
> create_symref("HEAD", our->name, NULL);
> if (!option_bare) {
I think this makes sense. In the following three casees:
- When cloning without --branch, if we could not find a branch that match
HEAD at the remote;
- When cloning with --branch, if we did not see such a branch at the
remote; or
- When cloning from an empty repository
we come here with "our" set to NULL. Additionally, if the remote HEAD
points outside refs/heads/ and the transport could detect that case
(e.g. a helper that reads from ls-remote output), we can see our->name
outside refs/heads/. Is there any other case where our is not NULL and
our->name does not start with refs/heads/?
The "else if (remote)" clause (not shown, outside the context) that
follows still has comments that says it is a case for "Source had detached
HEAD pointing somewhere", and needs to be adjusted for this change, no? It
is "(1) we know the HEAD points at a non-branch, (2) HEAD may point at a
branch but we do not know which one, or (3) we asked for a specific branch
but it did not exist there" (cloning from an empty will have NULL in
remote and the comment would not apply to that case).
> @@ -760,7 +760,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>
> if (option_branch) {
> struct strbuf head = STRBUF_INIT;
> - strbuf_addstr(&head, src_ref_prefix);
> + strbuf_addstr(&head, "refs/heads/");
> strbuf_addstr(&head, option_branch);
> our_head_points_at =
> find_ref_by_name(mapped_refs, head.buf);
^ permalink raw reply
* Re: leaky cherry-pick
From: Jeff King @ 2012-01-11 19:56 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: Junio C Hamano, Pete Wyckoff, git,
Nguyễn Thái Ngọc
In-Reply-To: <CALkWK0m+okqJk05BMQAEMww6FNLxaLVhAM92WmUDeA_J-drOdg@mail.gmail.com>
On Wed, Jan 11, 2012 at 02:30:16PM +0530, Ramkumar Ramachandra wrote:
> > I somehow have a feeling that you did not read the conclusion in Peff's
> > message correctly. The code only keeps data from one active path of
> > per-directory .gitattributes files to the leaf of a working tree and
> > releases unneeded data (IOW, it "pops" the attr_stack elements) when it
> > goes on to look at the next path, so my understanding is that there is
> > nothing to "try implementing" here.
>
> My bad- I thought the current implementation doesn't release the
> unneeded data. So, does the entire 7 KB of leaked data come from one
> active path?
Hmm. Actually, I think there is a leak. Because the strategy I described
leaves the memory reachable from the global attr_stack variable, which
means valgrind will only show it if "--show-reachable" is turned on.
Maybe this?
diff --git a/attr.c b/attr.c
index 76b079f..1656db4 100644
--- a/attr.c
+++ b/attr.c
@@ -301,6 +301,7 @@ static void free_attr_elem(struct attr_stack *e)
}
free(a);
}
+ free(e->attrs);
free(e);
}
-Peff
^ permalink raw reply related
* [PATCH 2/2] archive: loosen restrictions on remote object lookup
From: Jeff King @ 2012-01-11 19:42 UTC (permalink / raw)
To: Carlos Martín Nieto; +Cc: git, Albert Astals Cid
In-Reply-To: <20120111193916.GA12333@sigill.intra.peff.net>
Initially, "git upload-archive" would feed the tree
specification from the remote side directly into get_sha1,
giving the remote user the full power of the object name
resolver. This was convenient, but it also meant that remote
users could fetch disconnected trees by their sha1s, which
violates the long-standing behavior of upload-pack not to
make such objects available.
Later, commit ee27ca4 tightened this to use dwim_ref instead
of get_sha1 for the remote case, allowing only the use of
actual refs. Unfortunately, this broke some existing use
cases, like fetching sub-trees with "$ref:subdir".
This patch loosens the restrictions to re-enable those use
cases. It does this by using get_sha1_with_context for the
object lookup, and checking that only allowable features
were used.
Signed-off-by: Jeff King <peff@peff.net>
---
archive.c | 34 ++++++++++++++-------
t/t5002-archive-resolution.sh | 66 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 11 deletions(-)
create mode 100755 t/t5002-archive-resolution.sh
diff --git a/archive.c b/archive.c
index 164bbd0..a031bde 100644
--- a/archive.c
+++ b/archive.c
@@ -246,6 +246,25 @@ static void parse_pathspec_arg(const char **pathspec,
}
}
+static int check_object_context(int remote, const struct object_context *oc)
+{
+ /* For local requests, allow anything */
+ if (!remote)
+ return 1;
+ /*
+ * Otherwise, require that we accessed the object through a ref,
+ * but not have used any of the advanced features like looking in
+ * the reflog.
+ */
+ return oc->used_ref &&
+ !oc->used_reflog &&
+ !oc->used_index &&
+ !oc->used_nth_checkout &&
+ !oc->used_describe_name &&
+ !oc->used_oneline &&
+ !oc->used_raw_hex;
+}
+
static void parse_treeish_arg(const char **argv,
struct archiver_args *ar_args, const char *prefix,
int remote)
@@ -256,18 +275,11 @@ static void parse_treeish_arg(const char **argv,
struct tree *tree;
const struct commit *commit;
unsigned char sha1[20];
+ struct object_context oc;
- /* Remotes are only allowed to fetch actual refs */
- if (remote) {
- char *ref = NULL;
- if (!dwim_ref(name, strlen(name), sha1, &ref))
- die("no such ref: %s", name);
- free(ref);
- }
- else {
- if (get_sha1(name, sha1))
- die("Not a valid object name");
- }
+ if (get_sha1_with_context(name, sha1, &oc) ||
+ !check_object_context(remote, &oc))
+ die("Not a valid object name");
commit = lookup_commit_reference_gently(sha1, 1);
if (commit) {
diff --git a/t/t5002-archive-resolution.sh b/t/t5002-archive-resolution.sh
new file mode 100755
index 0000000..bf2b55c
--- /dev/null
+++ b/t/t5002-archive-resolution.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='test object resolution methods for local and remote archive'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo a >a &&
+ git add . &&
+ git commit -m one &&
+ sha1_one=`git rev-parse HEAD` &&
+ mkdir subdir &&
+ echo b >subdir/b &&
+ git add . &&
+ git commit -m two &&
+ git checkout -b other &&
+ git checkout master
+'
+
+while read desc where what expect; do
+ cmd="git archive --format=tar -o result.tar"
+ test "$where" = "remote" && cmd="$cmd --remote=."
+ cmd="$cmd $what"
+
+ if test "$expect" = "deny"; then
+ test_expect_success "archive $desc ($where, should deny)" "
+ test_must_fail $cmd
+ "
+ else
+ test_expect_success "archive $desc ($where, should work)" '
+ '"$cmd"' &&
+ for i in '"$expect"'; do
+ echo "$i:`basename $i`"
+ done >expect &&
+ rm -rf result &&
+ mkdir result &&
+ (cd result &&
+ tar xf ../result.tar &&
+ for i in `find * -type f`; do
+ echo "$i:`cat $i`"
+ done >../actual
+ ) &&
+ test_cmp expect actual
+ '
+ fi
+done <<EOF
+ref local master a subdir/b
+ref remote master a subdir/b
+parent local master^ a
+parent remote master^ a
+tree local master^{tree} a subdir/b
+tree remote master^{tree} a subdir/b
+subtree local master:subdir b
+subtree remote master:subdir b
+sha1 local $sha1_one a
+sha1 remote $sha1_one deny
+reflog local master@{1} a
+reflog remote master@{1} deny
+oneline local :/one a
+oneline remote :/one deny
+oneline-ref local master^{/one} a
+oneline-ref remote master^{/one} deny
+nth-checkout local @{-1} a subdir/b
+nth-checkout remote @{-1} deny
+EOF
+
+test_done
--
1.7.9.rc0.33.gd3c17
^ permalink raw reply related
* [PATCH 1/2] get_sha1_with_context: report features used in resolution
From: Jeff King @ 2012-01-11 19:42 UTC (permalink / raw)
To: Carlos Martín Nieto; +Cc: git, Albert Astals Cid
In-Reply-To: <20120111193916.GA12333@sigill.intra.peff.net>
Most callers generally treat get_sha1 as a black box, giving
it a string from the user and expecting to get a sha1 in
return. The get_sha1_with_context function gives callers
more information about what happened while resolving the
object name so they can make better decisions about how to
use the result. We currently use this only to provide
information about the path entry used to find a blob.
We don't currently provide any information about the
resolution rules that were used to reach the final object.
Some callers may want these in order to enforce a policy
that a particular subset of the lookup rules are used (e.g.,
when serving remote requests).
This patch adds a set of bit-fields that document the use of
particular features during an object lookup.
Signed-off-by: Jeff King <peff@peff.net>
---
The diffstat looks a little scary, but it is mostly just the internal
get_sha1 functions learning to pass the object_context around.
cache.h | 7 +++++
sha1_name.c | 73 +++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/cache.h b/cache.h
index 10afd71..ac25a37 100644
--- a/cache.h
+++ b/cache.h
@@ -809,6 +809,13 @@ struct object_context {
unsigned char tree[20];
char path[PATH_MAX];
unsigned mode;
+ unsigned used_ref:1,
+ used_reflog:1,
+ used_index:1,
+ used_nth_checkout:1,
+ used_describe_name:1,
+ used_oneline:1,
+ used_raw_hex:1;
};
extern int get_sha1(const char *str, unsigned char *sha1);
diff --git a/sha1_name.c b/sha1_name.c
index 03ffc2c..ad7c52a 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -7,7 +7,8 @@
#include "refs.h"
#include "remote.h"
-static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
+static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *,
+ struct object_context *);
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
{
@@ -158,7 +159,7 @@ static int find_unique_short_object(int len, char *canonical,
}
static int get_short_sha1(const char *name, int len, unsigned char *sha1,
- int quietly)
+ int quietly, struct object_context *oc)
{
int i, status;
char canonical[40];
@@ -190,6 +191,8 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
status = find_unique_short_object(i, canonical, res, sha1);
if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
return error("short SHA1 %.*s is ambiguous.", len, canonical);
+ if (!status)
+ oc->used_raw_hex = 1;
return status;
}
@@ -204,7 +207,8 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
return hex;
while (len < 40) {
unsigned char sha1_ret[20];
- status = get_short_sha1(hex, len, sha1_ret, 1);
+ struct object_context unused;
+ status = get_short_sha1(hex, len, sha1_ret, 1, &unused);
if (exists
? !status
: status == SHORT_NAME_NOT_FOUND) {
@@ -255,17 +259,21 @@ static inline int upstream_mark(const char *string, int len)
return 0;
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+static int get_sha1_1(const char *name, int len, unsigned char *sha1,
+ struct object_context *oc);
-static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
+static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
+ struct object_context *oc)
{
static const char *warn_msg = "refname '%.*s' is ambiguous.";
char *real_ref = NULL;
int refs_found = 0;
int at, reflog_len;
- if (len == 40 && !get_sha1_hex(str, sha1))
+ if (len == 40 && !get_sha1_hex(str, sha1)) {
+ oc->used_raw_hex = 1;
return 0;
+ }
/* basic@{time or number or -number} format to query ref-log */
reflog_len = at = 0;
@@ -292,7 +300,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
ret = interpret_branch_name(str+at, &buf);
if (ret > 0) {
/* substitute this branch name and restart */
- return get_sha1_1(buf.buf, buf.len, sha1);
+ oc->used_nth_checkout = 1;
+ return get_sha1_1(buf.buf, buf.len, sha1, oc);
} else if (ret == 0) {
return -1;
}
@@ -305,6 +314,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (!refs_found)
return -1;
+ oc->used_ref = 1;
if (warn_ambiguous_refs && refs_found > 1)
warning(warn_msg, len, str);
@@ -352,6 +362,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
len, str, co_cnt);
}
}
+ oc->used_reflog = 1;
}
free(real_ref);
@@ -359,10 +370,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
}
static int get_parent(const char *name, int len,
- unsigned char *result, int idx)
+ unsigned char *result, int idx,
+ struct object_context *oc)
{
unsigned char sha1[20];
- int ret = get_sha1_1(name, len, sha1);
+ int ret = get_sha1_1(name, len, sha1, oc);
struct commit *commit;
struct commit_list *p;
@@ -389,13 +401,14 @@ static int get_parent(const char *name, int len,
}
static int get_nth_ancestor(const char *name, int len,
- unsigned char *result, int generation)
+ unsigned char *result, int generation,
+ struct object_context *oc)
{
unsigned char sha1[20];
struct commit *commit;
int ret;
- ret = get_sha1_1(name, len, sha1);
+ ret = get_sha1_1(name, len, sha1, oc);
if (ret)
return ret;
commit = lookup_commit_reference(sha1);
@@ -436,7 +449,8 @@ struct object *peel_to_type(const char *name, int namelen,
}
}
-static int peel_onion(const char *name, int len, unsigned char *sha1)
+static int peel_onion(const char *name, int len, unsigned char *sha1,
+ struct object_context *oc)
{
unsigned char outer[20];
const char *sp;
@@ -476,7 +490,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
else
return -1;
- if (get_sha1_1(name, sp - name - 2, outer))
+ if (get_sha1_1(name, sp - name - 2, outer, oc))
return -1;
o = parse_object(outer);
@@ -515,14 +529,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
commit_list_insert((struct commit *)o, &list);
- ret = get_sha1_oneline(prefix, sha1, list);
+ ret = get_sha1_oneline(prefix, sha1, list, oc);
free(prefix);
return ret;
}
return 0;
}
-static int get_describe_name(const char *name, int len, unsigned char *sha1)
+static int get_describe_name(const char *name, int len, unsigned char *sha1,
+ struct object_context *oc)
{
const char *cp;
@@ -535,14 +550,15 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1)
if (ch == 'g' && cp[-1] == '-') {
cp++;
len -= cp - name;
- return get_short_sha1(cp, len, sha1, 1);
+ return get_short_sha1(cp, len, sha1, 1, oc);
}
}
}
return -1;
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1)
+static int get_sha1_1(const char *name, int len, unsigned char *sha1,
+ struct object_context *oc)
{
int ret, has_suffix;
const char *cp;
@@ -569,25 +585,25 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
if (!num && len1 == len - 1)
num = 1;
if (has_suffix == '^')
- return get_parent(name, len1, sha1, num);
+ return get_parent(name, len1, sha1, num, oc);
/* else if (has_suffix == '~') -- goes without saying */
- return get_nth_ancestor(name, len1, sha1, num);
+ return get_nth_ancestor(name, len1, sha1, num, oc);
}
- ret = peel_onion(name, len, sha1);
+ ret = peel_onion(name, len, sha1, oc);
if (!ret)
return 0;
- ret = get_sha1_basic(name, len, sha1);
+ ret = get_sha1_basic(name, len, sha1, oc);
if (!ret)
return 0;
/* It could be describe output that is "SOMETHING-gXXXX" */
- ret = get_describe_name(name, len, sha1);
+ ret = get_describe_name(name, len, sha1, oc);
if (!ret)
return 0;
- return get_short_sha1(name, len, sha1, 0);
+ return get_short_sha1(name, len, sha1, 0, oc);
}
/*
@@ -619,7 +635,8 @@ static int handle_one_ref(const char *path,
}
static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
- struct commit_list *list)
+ struct commit_list *list,
+ struct object_context *oc)
{
struct commit_list *backup = NULL, *l;
int found = 0;
@@ -672,6 +689,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
for (l = backup; l; l = l->next)
clear_commit_marks(l->item, ONELINE_SEEN);
free_commit_list(backup);
+ oc->used_oneline = found;
return found ? 0 : -1;
}
@@ -1029,7 +1047,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
memset(oc, 0, sizeof(*oc));
oc->mode = S_IFINVALID;
- ret = get_sha1_1(name, namelen, sha1);
+ ret = get_sha1_1(name, namelen, sha1, oc);
if (!ret)
return ret;
/* sha1:path --> object name of path in ent sha1
@@ -1046,7 +1064,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
if (!only_to_die && namelen > 2 && name[1] == '/') {
struct commit_list *list = NULL;
for_each_ref(handle_one_ref, &list);
- return get_sha1_oneline(name + 2, sha1, list);
+ return get_sha1_oneline(name + 2, sha1, list, oc);
}
if (namelen < 3 ||
name[2] != ':' ||
@@ -1081,6 +1099,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
if (ce_stage(ce) == stage) {
hashcpy(sha1, ce->sha1);
oc->mode = ce->ce_mode;
+ oc->used_index = 1;
free(new_path);
return 0;
}
@@ -1107,7 +1126,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
strncpy(object_name, name, cp-name);
object_name[cp-name] = '\0';
}
- if (!get_sha1_1(name, cp-name, tree_sha1)) {
+ if (!get_sha1_1(name, cp-name, tree_sha1, oc)) {
const char *filename = cp+1;
char *new_filename = NULL;
--
1.7.9.rc0.33.gd3c17
^ permalink raw reply related
* Re: [PATCH] archive: re-allow HEAD:Documentation on a remote invocation
From: Jeff King @ 2012-01-11 19:39 UTC (permalink / raw)
To: Carlos Martín Nieto; +Cc: git, Albert Astals Cid
In-Reply-To: <1326283958-30271-1-git-send-email-cmn@elego.de>
On Wed, Jan 11, 2012 at 01:12:38PM +0100, Carlos Martín Nieto wrote:
> The tightening done in (ee27ca4a: archive: don't let remote clients
> get unreachable commits, 2011-11-17) went too far and disallowed
> HEAD:Documentation as it would try to find "HEAD:Documentation" as a
> ref.
>
> Only DWIM the "HEAD" part to see if it exists as a ref. Once we're
> sure that we've been given a valid ref, we follow the normal code
> path. This still disallows attempts to access commits which are not
> branch tips.
I'd rather not do this kind of ad-hoc parsing of sha1s in the archive
code, and instead let the regular resolution process tell us more about
what it did, so we can make a policy decision at the upper level.
Patches to follow:
[1/2]: get_sha1_with_context: report features used in resolution
[2/2]: archive: loosen restrictions on remote object lookup
> AFAICT this should still be safe. Using HEAD^:Documentation or
> <sha1>:Documentation still complains that HEAD^ and <sha1> aren't
> refs.
My patches enable things like HEAD^, but disallow a raw sha1. The only
way to safely allow a raw sha1 is to check its connectivity from the
tips, which can be somewhat expensive (you have to traverse every tree
of every commit in the worst case).
-Peff
^ permalink raw reply
* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
From: Junio C Hamano @ 2012-01-11 19:36 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King
In-Reply-To: <1326189427-20800-7-git-send-email-pclouds@gmail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> This gives us an opportunity to abort the command during remote HEAD
> check without wasting much bandwidth.
>
> Cloning with remote-helper remains before the check because the remote
> helper updates mapped_refs, which is necessary for remote ref checks.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> I'm not familiar with remote-helper to see if there's any better way
> to do this..
> ...
> + refs = transport_get_remote_refs(transport);
> + mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
> +
> + /*
> + * mapped_refs may be updated if transport-helper is used so
> + * we need fetch it early because remote_head code below
> + * relies on it.
> + *
> + * for normal clones, transport_get_remote_refs() should
> + * return reliable ref set, we can delay cloning until after
> + * remote HEAD check.
> + */
> + if (!is_local && remote->foreign_vcs && refs)
> + transport_fetch_refs(transport, mapped_refs);
> +
I like the goal of this change, but wouldn't remote->url indicate it is a
remote that needs a helper invocation by having double-colon in it,
without having an explicit value in foreign_vcs field?
Would it be cleaner if transport_get() learned to mark the transport as
needing special treatment (i.e. we won't know the final ref mapping until
we fetch the data from the other side) and check is made on that mark left
in the transport, instead of foreign_vcs in remote?
> diff --git a/transport.c b/transport.c
> index a99b7c9..aae9889 100644
> --- a/transport.c
> +++ b/transport.c
> @@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
>
> while (is_urlschemechar(p == url, *p))
> p++;
> - if (!prefixcmp(p, "::"))
> + if (!prefixcmp(p, "::")) {
> helper = xstrndup(url, p - url);
> + remote->foreign_vcs = helper;
> + }
> }
>
> if (helper) {
Ahhh, Ok. You are reusing the existing "foreign_vcs" field exactly for
that purpose. Earlier the field was strictly for configured .vcs value,
but now you use it also for the helper embedded within the URL, which
sounds like the right change to me.
> @@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
> char *handler = xmalloc(len + 1);
> handler[len] = 0;
> strncpy(handler, url, len);
> + remote->foreign_vcs = helper;
> transport_helper_init(ret, handler);
> }
This I am not sure. What value does "helper" variable have at this point
in the flow? Wouldn't it be a NULL? Or did you mean "handler"?
^ permalink raw reply
* Re: Regulator updates for 3.3
From: Paul Gortmaker @ 2012-01-11 18:40 UTC (permalink / raw)
To: Linus Torvalds
Cc: Mark Brown, Liam Girdwood, linux-kernel, Junio C Hamano,
Git Mailing List
In-Reply-To: <CA+55aFxvQF=Bm4ae6euB_UO8otMCuN9Lv37Zn3TpE-L7JH3Kzw@mail.gmail.com>
[Re: Regulator updates for 3.3] On 10/01/2012 (Tue 14:54) Linus Torvalds wrote:
[...]
> So right now "git merge" (and "git pull") make it too easy to make
> those meaningless merge commits. If instead of seven pointless merges
It looks like the editor-by-default solution is a go, but there still
might be value in increasing the visibility of the pointless merges
via. the patch below.
Paul.
> you had (say) had two merges that had messages about *why* they
> weren't pointless, I'd be perfectly happy.
>
> Addid junio and git to the cc just to bring up this issue of bad UI
> once again. I realize it could break old scripts to start up an editor
> window, but still..
>
> Linus
>From 1a548fa97b78cebcded15d2b00ee3d826f731abd Mon Sep 17 00:00:00 2001
From: Paul Gortmaker <paul.gortmaker@windriver.com>
Date: Wed, 11 Jan 2012 10:33:45 -0500
Subject: [PATCH] merge: Make merge strategy message follow the diffstat
One of the common problems I've seen with people who are
somewhat new to git is that they don't realize that a pull
is a fetch+merge. They simply decide they want all the
latest stuff and issue a git pull without really thinking
if they are on a branch with local commits or on master,
where a fast forward can take place.
But the one line message that tells you whether you got a fast
forward or a real merge commit is usually pushed off the
screen by all the diffstat information. So these users won't
even know that their pull has created a merge, and chances
are they will never change their workflow.
By moving the message after the diffstat, there is a better
chance that people will be aware they've done a pointless
merge commit.
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
diff --git a/builtin/merge.c b/builtin/merge.c
index 3a45172..9471588 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -370,12 +370,12 @@ static void finish(struct commit *head_commit,
{
struct strbuf reflog_message = STRBUF_INIT;
const unsigned char *head = head_commit->object.sha1;
+ int automsg = 0;
- if (!msg)
+ if (!msg) {
+ automsg = 1;
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
- else {
- if (verbosity >= 0)
- printf("%s\n", msg);
+ } else {
strbuf_addf(&reflog_message, "%s: %s",
getenv("GIT_REFLOG_ACTION"), msg);
}
@@ -409,6 +409,9 @@ static void finish(struct commit *head_commit,
diff_flush(&opts);
}
+ if (!automsg && verbosity >= 0)
+ printf("%s\n", msg);
+
/* Run a post-merge hook */
run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
--
1.7.4.4
^ permalink raw reply related
* Re: [PATCH v3 0/2] The move to sequencer.c
From: Jonathan Nieder @ 2012-01-11 18:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ramkumar Ramachandra, Git List, Christian Couder
In-Reply-To: <1326305757-27525-1-git-send-email-artagnon@gmail.com>
Ramkumar Ramachandra wrote:
> Ramkumar Ramachandra (2):
> revert: prepare to move replay_action to header
> sequencer: factor code out of revert builtin
Ah. "git diff HEAD^:builtin/revert.c HEAD:sequencer.c" gives a sane
diff, and the remaining stuff in builtin/revert.c feels pleasant.
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Here's a patchlet for squashing into patch 2/2.
---
builtin/revert.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 4116f2d3..e6840f23 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -30,8 +30,6 @@ static const char * const cherry_pick_usage[] = {
NULL
};
-#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-
static const char *action_name(const struct replay_opts *opts)
{
return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
--
^ permalink raw reply related
* [PATCH 2/2] sequencer: factor code out of revert builtin
From: Ramkumar Ramachandra @ 2012-01-11 18:15 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder
In-Reply-To: <1326305757-27525-1-git-send-email-artagnon@gmail.com>
Expose the cherry-picking machinery through a public
sequencer_pick_revisions() (renamed from pick_revisions() in
builtin/revert.c), so that cherry-picking and reverting are special
cases of a general sequencer operation. The cherry-pick builtin is
now a thin wrapper that does command-line argument parsing before
calling into sequencer_pick_revisions(). In the future, we can write
a new "foo" builtin that calls into the sequencer like:
memset(&opts, 0, sizeof(opts));
opts.action = REPLAY_FOO;
opts.revisions = xmalloc(sizeof(*opts.revs));
parse_args_populate_opts(argc, argv, &opts);
init_revisions(opts.revs);
sequencer_pick_revisions(&opts);
This patch does not intend to make any functional changes. Check
with:
$ git blame -s -C HEAD^..HEAD -- sequencer.c | grep -C3 '^[^^]'
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
builtin/revert.c | 946 +-----------------------------------------------------
sequencer.c | 918 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
sequencer.h | 37 +++
3 files changed, 956 insertions(+), 945 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 2739405..4116f2d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,18 +1,9 @@
#include "cache.h"
#include "builtin.h"
-#include "object.h"
-#include "commit.h"
-#include "tag.h"
-#include "run-command.h"
-#include "exec_cmd.h"
-#include "utf8.h"
#include "parse-options.h"
-#include "cache-tree.h"
#include "diff.h"
#include "revision.h"
#include "rerere.h"
-#include "merge-recursive.h"
-#include "refs.h"
#include "dir.h"
#include "sequencer.h"
@@ -39,41 +30,6 @@ static const char * const cherry_pick_usage[] = {
NULL
};
-enum replay_action {
- REPLAY_REVERT,
- REPLAY_PICK
-};
-
-enum replay_subcommand {
- REPLAY_NONE,
- REPLAY_REMOVE_STATE,
- REPLAY_CONTINUE,
- REPLAY_ROLLBACK
-};
-
-struct replay_opts {
- enum replay_action action;
- enum replay_subcommand subcommand;
-
- /* Boolean options */
- int edit;
- int record_origin;
- int no_commit;
- int signoff;
- int allow_ff;
- int allow_rerere_auto;
-
- int mainline;
-
- /* Merge strategy */
- const char *strategy;
- const char **xopts;
- size_t xopts_nr, xopts_alloc;
-
- /* Only used by REPLAY_NONE */
- struct rev_info *revs;
-};
-
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
static const char *action_name(const struct replay_opts *opts)
@@ -81,8 +37,6 @@ static const char *action_name(const struct replay_opts *opts)
return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
}
-static char *get_encoding(const char *message);
-
static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
{
return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
@@ -241,902 +195,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
usage_with_options(usage_str, options);
}
-struct commit_message {
- char *parent_label;
- const char *label;
- const char *subject;
- char *reencoded_message;
- const char *message;
-};
-
-static int get_message(struct commit *commit, struct commit_message *out)
-{
- const char *encoding;
- const char *abbrev, *subject;
- int abbrev_len, subject_len;
- char *q;
-
- if (!commit->buffer)
- return -1;
- encoding = get_encoding(commit->buffer);
- if (!encoding)
- encoding = "UTF-8";
- if (!git_commit_encoding)
- git_commit_encoding = "UTF-8";
-
- out->reencoded_message = NULL;
- out->message = commit->buffer;
- if (strcmp(encoding, git_commit_encoding))
- out->reencoded_message = reencode_string(commit->buffer,
- git_commit_encoding, encoding);
- if (out->reencoded_message)
- out->message = out->reencoded_message;
-
- abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
- abbrev_len = strlen(abbrev);
-
- subject_len = find_commit_subject(out->message, &subject);
-
- out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + subject_len + 1);
- q = out->parent_label;
- q = mempcpy(q, "parent of ", strlen("parent of "));
- out->label = q;
- q = mempcpy(q, abbrev, abbrev_len);
- q = mempcpy(q, "... ", strlen("... "));
- out->subject = q;
- q = mempcpy(q, subject, subject_len);
- *q = '\0';
- return 0;
-}
-
-static void free_message(struct commit_message *msg)
-{
- free(msg->parent_label);
- free(msg->reencoded_message);
-}
-
-static char *get_encoding(const char *message)
-{
- const char *p = message, *eol;
-
- while (*p && *p != '\n') {
- for (eol = p + 1; *eol && *eol != '\n'; eol++)
- ; /* do nothing */
- if (!prefixcmp(p, "encoding ")) {
- char *result = xmalloc(eol - 8 - p);
- strlcpy(result, p + 9, eol - 8 - p);
- return result;
- }
- p = eol;
- if (*p == '\n')
- p++;
- }
- return NULL;
-}
-
-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
-{
- const char *filename;
- int fd;
- struct strbuf buf = STRBUF_INIT;
-
- strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
-
- filename = git_path("%s", pseudoref);
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"), filename);
- if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
- die_errno(_("Could not write to '%s'"), filename);
- strbuf_release(&buf);
-}
-
-static void print_advice(int show_hint)
-{
- char *msg = getenv("GIT_CHERRY_PICK_HELP");
-
- if (msg) {
- fprintf(stderr, "%s\n", msg);
- /*
- * A conflict has occured but the porcelain
- * (typically rebase --interactive) wants to take care
- * of the commit itself so remove CHERRY_PICK_HEAD
- */
- unlink(git_path("CHERRY_PICK_HEAD"));
- return;
- }
-
- if (show_hint) {
- advise("after resolving the conflicts, mark the corrected paths");
- advise("with 'git add <paths>' or 'git rm <paths>'");
- advise("and commit the result with 'git commit'");
- }
-}
-
-static void write_message(struct strbuf *msgbuf, const char *filename)
-{
- static struct lock_file msg_file;
-
- int msg_fd = hold_lock_file_for_update(&msg_file, filename,
- LOCK_DIE_ON_ERROR);
- if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
- die_errno(_("Could not write to %s"), filename);
- strbuf_release(msgbuf);
- if (commit_lock_file(&msg_file) < 0)
- die(_("Error wrapping up %s"), filename);
-}
-
-static struct tree *empty_tree(void)
-{
- return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
-}
-
-static int error_dirty_index(struct replay_opts *opts)
-{
- if (read_cache_unmerged())
- return error_resolve_conflict(action_name(opts));
-
- /* Different translation strings for cherry-pick and revert */
- if (opts->action == REPLAY_PICK)
- error(_("Your local changes would be overwritten by cherry-pick."));
- else
- error(_("Your local changes would be overwritten by revert."));
-
- if (advice_commit_before_merge)
- advise(_("Commit your changes or stash them to proceed."));
- return -1;
-}
-
-static int fast_forward_to(const unsigned char *to, const unsigned char *from)
-{
- struct ref_lock *ref_lock;
-
- read_cache();
- if (checkout_fast_forward(from, to))
- exit(1); /* the callee should have complained already */
- ref_lock = lock_any_ref_for_update("HEAD", from, 0);
- return write_ref_sha1(ref_lock, to, "cherry-pick");
-}
-
-static int do_recursive_merge(struct commit *base, struct commit *next,
- const char *base_label, const char *next_label,
- unsigned char *head, struct strbuf *msgbuf,
- struct replay_opts *opts)
-{
- struct merge_options o;
- struct tree *result, *next_tree, *base_tree, *head_tree;
- int clean, index_fd;
- const char **xopt;
- static struct lock_file index_lock;
-
- index_fd = hold_locked_index(&index_lock, 1);
-
- read_cache();
-
- init_merge_options(&o);
- o.ancestor = base ? base_label : "(empty tree)";
- o.branch1 = "HEAD";
- o.branch2 = next ? next_label : "(empty tree)";
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
-
- for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
- parse_merge_opt(&o, *xopt);
-
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
-
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
- die(_("%s: Unable to write new index file"), action_name(opts));
- rollback_lock_file(&index_lock);
-
- if (!clean) {
- int i;
- strbuf_addstr(msgbuf, "\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- strbuf_addch(msgbuf, '\t');
- strbuf_addstr(msgbuf, ce->name);
- strbuf_addch(msgbuf, '\n');
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
- }
- }
-
- return !clean;
-}
-
-/*
- * If we are cherry-pick, and if the merge did not result in
- * hand-editing, we will hit this commit and inherit the original
- * author date and name.
- * If we are revert, or if our cherry-pick results in a hand merge,
- * we had better say that the current user is responsible for that.
- */
-static int run_git_commit(const char *defmsg, struct replay_opts *opts)
-{
- /* 6 is max possible length of our args array including NULL */
- const char *args[6];
- int i = 0;
-
- args[i++] = "commit";
- args[i++] = "-n";
- if (opts->signoff)
- args[i++] = "-s";
- if (!opts->edit) {
- args[i++] = "-F";
- args[i++] = defmsg;
- }
- args[i] = NULL;
-
- return run_command_v_opt(args, RUN_GIT_CMD);
-}
-
-static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
-{
- unsigned char head[20];
- struct commit *base, *next, *parent;
- const char *base_label, *next_label;
- struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
- char *defmsg = NULL;
- struct strbuf msgbuf = STRBUF_INIT;
- int res;
-
- if (opts->no_commit) {
- /*
- * We do not intend to commit immediately. We just want to
- * merge the differences in, so let's compute the tree
- * that represents the "current" state for merge-recursive
- * to work on.
- */
- if (write_cache_as_tree(head, 0, NULL))
- die (_("Your index file is unmerged."));
- } else {
- if (get_sha1("HEAD", head))
- return error(_("You do not have a valid HEAD"));
- if (index_differs_from("HEAD", 0))
- return error_dirty_index(opts);
- }
- discard_cache();
-
- if (!commit->parents) {
- parent = NULL;
- }
- else if (commit->parents->next) {
- /* Reverting or cherry-picking a merge commit */
- int cnt;
- struct commit_list *p;
-
- if (!opts->mainline)
- return error(_("Commit %s is a merge but no -m option was given."),
- sha1_to_hex(commit->object.sha1));
-
- for (cnt = 1, p = commit->parents;
- cnt != opts->mainline && p;
- cnt++)
- p = p->next;
- if (cnt != opts->mainline || !p)
- return error(_("Commit %s does not have parent %d"),
- sha1_to_hex(commit->object.sha1), opts->mainline);
- parent = p->item;
- } else if (0 < opts->mainline)
- return error(_("Mainline was specified but commit %s is not a merge."),
- sha1_to_hex(commit->object.sha1));
- else
- parent = commit->parents->item;
-
- if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
- return fast_forward_to(commit->object.sha1, head);
-
- if (parent && parse_commit(parent) < 0)
- /* TRANSLATORS: The first %s will be "revert" or
- "cherry-pick", the second %s a SHA1 */
- return error(_("%s: cannot parse parent commit %s"),
- action_name(opts), sha1_to_hex(parent->object.sha1));
-
- if (get_message(commit, &msg) != 0)
- return error(_("Cannot get commit message for %s"),
- sha1_to_hex(commit->object.sha1));
-
- /*
- * "commit" is an existing commit. We would want to apply
- * the difference it introduces since its first parent "prev"
- * on top of the current HEAD if we are cherry-pick. Or the
- * reverse of it if we are revert.
- */
-
- defmsg = git_pathdup("MERGE_MSG");
-
- if (opts->action == REPLAY_REVERT) {
- base = commit;
- base_label = msg.label;
- next = parent;
- next_label = msg.parent_label;
- strbuf_addstr(&msgbuf, "Revert \"");
- strbuf_addstr(&msgbuf, msg.subject);
- strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
- strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
-
- if (commit->parents && commit->parents->next) {
- strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
- strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
- }
- strbuf_addstr(&msgbuf, ".\n");
- } else {
- const char *p;
-
- base = parent;
- base_label = msg.parent_label;
- next = commit;
- next_label = msg.label;
-
- /*
- * Append the commit log message to msgbuf; it starts
- * after the tree, parent, author, committer
- * information followed by "\n\n".
- */
- p = strstr(msg.message, "\n\n");
- if (p) {
- p += 2;
- strbuf_addstr(&msgbuf, p);
- }
-
- if (opts->record_origin) {
- strbuf_addstr(&msgbuf, "(cherry picked from commit ");
- strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
- strbuf_addstr(&msgbuf, ")\n");
- }
- }
-
- if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
- res = do_recursive_merge(base, next, base_label, next_label,
- head, &msgbuf, opts);
- write_message(&msgbuf, defmsg);
- } else {
- struct commit_list *common = NULL;
- struct commit_list *remotes = NULL;
-
- write_message(&msgbuf, defmsg);
-
- commit_list_insert(base, &common);
- commit_list_insert(next, &remotes);
- res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
- common, sha1_to_hex(head), remotes);
- free_commit_list(common);
- free_commit_list(remotes);
- }
-
- /*
- * If the merge was clean or if it failed due to conflict, we write
- * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
- * However, if the merge did not even start, then we don't want to
- * write it at all.
- */
- if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
- write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
- if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
- write_cherry_pick_head(commit, "REVERT_HEAD");
-
- if (res) {
- error(opts->action == REPLAY_REVERT
- ? _("could not revert %s... %s")
- : _("could not apply %s... %s"),
- find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
- msg.subject);
- print_advice(res == 1);
- rerere(opts->allow_rerere_auto);
- } else {
- if (!opts->no_commit)
- res = run_git_commit(defmsg, opts);
- }
-
- free_message(&msg);
- free(defmsg);
-
- return res;
-}
-
-static void prepare_revs(struct replay_opts *opts)
-{
- if (opts->action != REPLAY_REVERT)
- opts->revs->reverse ^= 1;
-
- if (prepare_revision_walk(opts->revs))
- die(_("revision walk setup failed"));
-
- if (!opts->revs->commits)
- die(_("empty commit set passed"));
-}
-
-static void read_and_refresh_cache(struct replay_opts *opts)
-{
- static struct lock_file index_lock;
- int index_fd = hold_locked_index(&index_lock, 0);
- if (read_index_preload(&the_index, NULL) < 0)
- die(_("git %s: failed to read the index"), action_name(opts));
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
- if (the_index.cache_changed) {
- if (write_index(&the_index, index_fd) ||
- commit_locked_index(&index_lock))
- die(_("git %s: failed to refresh the index"), action_name(opts));
- }
- rollback_lock_file(&index_lock);
-}
-
-/*
- * Append a commit to the end of the commit_list.
- *
- * next starts by pointing to the variable that holds the head of an
- * empty commit_list, and is updated to point to the "next" field of
- * the last item on the list as new commits are appended.
- *
- * Usage example:
- *
- * struct commit_list *list;
- * struct commit_list **next = &list;
- *
- * next = commit_list_append(c1, next);
- * next = commit_list_append(c2, next);
- * assert(commit_list_count(list) == 2);
- * return list;
- */
-static struct commit_list **commit_list_append(struct commit *commit,
- struct commit_list **next)
-{
- struct commit_list *new = xmalloc(sizeof(struct commit_list));
- new->item = commit;
- *next = new;
- new->next = NULL;
- return &new->next;
-}
-
-static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
- struct replay_opts *opts)
-{
- struct commit_list *cur = NULL;
- const char *sha1_abbrev = NULL;
- const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
- const char *subject;
- int subject_len;
-
- for (cur = todo_list; cur; cur = cur->next) {
- sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
- subject_len = find_commit_subject(cur->item->buffer, &subject);
- strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
- subject_len, subject);
- }
- return 0;
-}
-
-static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
-{
- unsigned char commit_sha1[20];
- enum replay_action action;
- char *end_of_object_name;
- int saved, status, padding;
-
- if (!prefixcmp(bol, "pick")) {
- action = REPLAY_PICK;
- bol += strlen("pick");
- } else if (!prefixcmp(bol, "revert")) {
- action = REPLAY_REVERT;
- bol += strlen("revert");
- } else
- return NULL;
-
- /* Eat up extra spaces/ tabs before object name */
- padding = strspn(bol, " \t");
- if (!padding)
- return NULL;
- bol += padding;
-
- end_of_object_name = bol + strcspn(bol, " \t\n");
- saved = *end_of_object_name;
- *end_of_object_name = '\0';
- status = get_sha1(bol, commit_sha1);
- *end_of_object_name = saved;
-
- /*
- * Verify that the action matches up with the one in
- * opts; we don't support arbitrary instructions
- */
- if (action != opts->action) {
- const char *action_str;
- action_str = action == REPLAY_REVERT ? "revert" : "cherry-pick";
- error(_("Cannot %s during a %s"), action_str, action_name(opts));
- return NULL;
- }
-
- if (status < 0)
- return NULL;
-
- return lookup_commit_reference(commit_sha1);
-}
-
-static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
- struct replay_opts *opts)
-{
- struct commit_list **next = todo_list;
- struct commit *commit;
- char *p = buf;
- int i;
-
- for (i = 1; *p; i++) {
- char *eol = strchrnul(p, '\n');
- commit = parse_insn_line(p, eol, opts);
- if (!commit)
- return error(_("Could not parse line %d."), i);
- next = commit_list_append(commit, next);
- p = *eol ? eol + 1 : eol;
- }
- if (!*todo_list)
- return error(_("No commits parsed."));
- return 0;
-}
-
-static void read_populate_todo(struct commit_list **todo_list,
- struct replay_opts *opts)
-{
- const char *todo_file = git_path(SEQ_TODO_FILE);
- struct strbuf buf = STRBUF_INIT;
- int fd, res;
-
- fd = open(todo_file, O_RDONLY);
- if (fd < 0)
- die_errno(_("Could not open %s"), todo_file);
- if (strbuf_read(&buf, fd, 0) < 0) {
- close(fd);
- strbuf_release(&buf);
- die(_("Could not read %s."), todo_file);
- }
- close(fd);
-
- res = parse_insn_buffer(buf.buf, todo_list, opts);
- strbuf_release(&buf);
- if (res)
- die(_("Unusable instruction sheet: %s"), todo_file);
-}
-
-static int populate_opts_cb(const char *key, const char *value, void *data)
-{
- struct replay_opts *opts = data;
- int error_flag = 1;
-
- if (!value)
- error_flag = 0;
- else if (!strcmp(key, "options.no-commit"))
- opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.edit"))
- opts->edit = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.signoff"))
- opts->signoff = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.record-origin"))
- opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.allow-ff"))
- opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.mainline"))
- opts->mainline = git_config_int(key, value);
- else if (!strcmp(key, "options.strategy"))
- git_config_string(&opts->strategy, key, value);
- else if (!strcmp(key, "options.strategy-option")) {
- ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
- opts->xopts[opts->xopts_nr++] = xstrdup(value);
- } else
- return error(_("Invalid key: %s"), key);
-
- if (!error_flag)
- return error(_("Invalid value for %s: %s"), key, value);
-
- return 0;
-}
-
-static void read_populate_opts(struct replay_opts **opts_ptr)
-{
- const char *opts_file = git_path(SEQ_OPTS_FILE);
-
- if (!file_exists(opts_file))
- return;
- if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
- die(_("Malformed options sheet: %s"), opts_file);
-}
-
-static void walk_revs_populate_todo(struct commit_list **todo_list,
- struct replay_opts *opts)
-{
- struct commit *commit;
- struct commit_list **next;
-
- prepare_revs(opts);
-
- next = todo_list;
- while ((commit = get_revision(opts->revs)))
- next = commit_list_append(commit, next);
-}
-
-static int create_seq_dir(void)
-{
- const char *seq_dir = git_path(SEQ_DIR);
-
- if (file_exists(seq_dir)) {
- error(_("a cherry-pick or revert is already in progress"));
- advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
- return -1;
- }
- else if (mkdir(seq_dir, 0777) < 0)
- die_errno(_("Could not create sequencer directory %s"), seq_dir);
- return 0;
-}
-
-static void save_head(const char *head)
-{
- const char *head_file = git_path(SEQ_HEAD_FILE);
- static struct lock_file head_lock;
- struct strbuf buf = STRBUF_INIT;
- int fd;
-
- fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
- strbuf_addf(&buf, "%s\n", head);
- if (write_in_full(fd, buf.buf, buf.len) < 0)
- die_errno(_("Could not write to %s"), head_file);
- if (commit_lock_file(&head_lock) < 0)
- die(_("Error wrapping up %s."), head_file);
-}
-
-static int reset_for_rollback(const unsigned char *sha1)
-{
- const char *argv[4]; /* reset --merge <arg> + NULL */
- argv[0] = "reset";
- argv[1] = "--merge";
- argv[2] = sha1_to_hex(sha1);
- argv[3] = NULL;
- return run_command_v_opt(argv, RUN_GIT_CMD);
-}
-
-static int rollback_single_pick(void)
-{
- unsigned char head_sha1[20];
-
- if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
- !file_exists(git_path("REVERT_HEAD")))
- return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", head_sha1, 0, NULL))
- return error(_("cannot resolve HEAD"));
- if (is_null_sha1(head_sha1))
- return error(_("cannot abort from a branch yet to be born"));
- return reset_for_rollback(head_sha1);
-}
-
-static int sequencer_rollback(struct replay_opts *opts)
-{
- const char *filename;
- FILE *f;
- unsigned char sha1[20];
- struct strbuf buf = STRBUF_INIT;
-
- filename = git_path(SEQ_HEAD_FILE);
- f = fopen(filename, "r");
- if (!f && errno == ENOENT) {
- /*
- * There is no multiple-cherry-pick in progress.
- * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
- * a single-cherry-pick in progress, abort that.
- */
- return rollback_single_pick();
- }
- if (!f)
- return error(_("cannot open %s: %s"), filename,
- strerror(errno));
- if (strbuf_getline(&buf, f, '\n')) {
- error(_("cannot read %s: %s"), filename, ferror(f) ?
- strerror(errno) : _("unexpected end of file"));
- fclose(f);
- goto fail;
- }
- fclose(f);
- if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
- error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
- filename);
- goto fail;
- }
- if (reset_for_rollback(sha1))
- goto fail;
- remove_sequencer_state();
- strbuf_release(&buf);
- return 0;
-fail:
- strbuf_release(&buf);
- return -1;
-}
-
-static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
-{
- const char *todo_file = git_path(SEQ_TODO_FILE);
- static struct lock_file todo_lock;
- struct strbuf buf = STRBUF_INIT;
- int fd;
-
- fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
- if (format_todo(&buf, todo_list, opts) < 0)
- die(_("Could not format %s."), todo_file);
- if (write_in_full(fd, buf.buf, buf.len) < 0) {
- strbuf_release(&buf);
- die_errno(_("Could not write to %s"), todo_file);
- }
- if (commit_lock_file(&todo_lock) < 0) {
- strbuf_release(&buf);
- die(_("Error wrapping up %s."), todo_file);
- }
- strbuf_release(&buf);
-}
-
-static void save_opts(struct replay_opts *opts)
-{
- const char *opts_file = git_path(SEQ_OPTS_FILE);
-
- if (opts->no_commit)
- git_config_set_in_file(opts_file, "options.no-commit", "true");
- if (opts->edit)
- git_config_set_in_file(opts_file, "options.edit", "true");
- if (opts->signoff)
- git_config_set_in_file(opts_file, "options.signoff", "true");
- if (opts->record_origin)
- git_config_set_in_file(opts_file, "options.record-origin", "true");
- if (opts->allow_ff)
- git_config_set_in_file(opts_file, "options.allow-ff", "true");
- if (opts->mainline) {
- struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%d", opts->mainline);
- git_config_set_in_file(opts_file, "options.mainline", buf.buf);
- strbuf_release(&buf);
- }
- if (opts->strategy)
- git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
- if (opts->xopts) {
- int i;
- for (i = 0; i < opts->xopts_nr; i++)
- git_config_set_multivar_in_file(opts_file,
- "options.strategy-option",
- opts->xopts[i], "^$", 0);
- }
-}
-
-static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
-{
- struct commit_list *cur;
- int res;
-
- setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
- if (opts->allow_ff)
- assert(!(opts->signoff || opts->no_commit ||
- opts->record_origin || opts->edit));
- read_and_refresh_cache(opts);
-
- for (cur = todo_list; cur; cur = cur->next) {
- save_todo(cur, opts);
- res = do_pick_commit(cur->item, opts);
- if (res)
- return res;
- }
-
- /*
- * Sequence of picks finished successfully; cleanup by
- * removing the .git/sequencer directory
- */
- remove_sequencer_state();
- return 0;
-}
-
-static int continue_single_pick(void)
-{
- const char *argv[] = { "commit", NULL };
-
- if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
- !file_exists(git_path("REVERT_HEAD")))
- return error(_("no cherry-pick or revert in progress"));
- return run_command_v_opt(argv, RUN_GIT_CMD);
-}
-
-static int sequencer_continue(struct replay_opts *opts)
-{
- struct commit_list *todo_list = NULL;
-
- if (!file_exists(git_path(SEQ_TODO_FILE)))
- return continue_single_pick();
- read_populate_opts(&opts);
- read_populate_todo(&todo_list, opts);
-
- /* Verify that the conflict has been resolved */
- if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
- file_exists(git_path("REVERT_HEAD"))) {
- int ret = continue_single_pick();
- if (ret)
- return ret;
- }
- if (index_differs_from("HEAD", 0))
- return error_dirty_index(opts);
- todo_list = todo_list->next;
- return pick_commits(todo_list, opts);
-}
-
-static int single_pick(struct commit *cmit, struct replay_opts *opts)
-{
- setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
- return do_pick_commit(cmit, opts);
-}
-
-static int pick_revisions(struct replay_opts *opts)
-{
- struct commit_list *todo_list = NULL;
- unsigned char sha1[20];
-
- if (opts->subcommand == REPLAY_NONE)
- assert(opts->revs);
-
- read_and_refresh_cache(opts);
-
- /*
- * Decide what to do depending on the arguments; a fresh
- * cherry-pick should be handled differently from an existing
- * one that is being continued
- */
- if (opts->subcommand == REPLAY_REMOVE_STATE) {
- remove_sequencer_state();
- return 0;
- }
- if (opts->subcommand == REPLAY_ROLLBACK)
- return sequencer_rollback(opts);
- if (opts->subcommand == REPLAY_CONTINUE)
- return sequencer_continue(opts);
-
- /*
- * If we were called as "git cherry-pick <commit>", just
- * cherry-pick/revert it, set CHERRY_PICK_HEAD /
- * REVERT_HEAD, and don't touch the sequencer state.
- * This means it is possible to cherry-pick in the middle
- * of a cherry-pick sequence.
- */
- if (opts->revs->cmdline.nr == 1 &&
- opts->revs->cmdline.rev->whence == REV_CMD_REV &&
- opts->revs->no_walk &&
- !opts->revs->cmdline.rev->flags) {
- struct commit *cmit;
- if (prepare_revision_walk(opts->revs))
- die(_("revision walk setup failed"));
- cmit = get_revision(opts->revs);
- if (!cmit || get_revision(opts->revs))
- die("BUG: expected exactly one commit from walk");
- return single_pick(cmit, opts);
- }
-
- /*
- * Start a new cherry-pick/ revert sequence; but
- * first, make sure that an existing one isn't in
- * progress
- */
-
- walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0)
- return -1;
- if (get_sha1("HEAD", sha1)) {
- if (opts->action == REPLAY_REVERT)
- return error(_("Can't revert as initial commit"));
- return error(_("Can't cherry-pick into empty head"));
- }
- save_head(sha1_to_hex(sha1));
- save_opts(opts);
- return pick_commits(todo_list, opts);
-}
-
int cmd_revert(int argc, const char **argv, const char *prefix)
{
struct replay_opts opts;
@@ -1148,7 +206,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_REVERT;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
- res = pick_revisions(&opts);
+ res = sequencer_pick_revisions(&opts);
if (res < 0)
die(_("revert failed"));
return res;
@@ -1163,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_PICK;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
- res = pick_revisions(&opts);
+ res = sequencer_pick_revisions(&opts);
if (res < 0)
die(_("cherry-pick failed"));
return res;
diff --git a/sequencer.c b/sequencer.c
index d1f28a6..5477119 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,7 +1,20 @@
#include "cache.h"
#include "sequencer.h"
-#include "strbuf.h"
#include "dir.h"
+#include "object.h"
+#include "commit.h"
+#include "tag.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+#include "utf8.h"
+#include "cache-tree.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
+#include "merge-recursive.h"
+#include "refs.h"
+
+#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
void remove_sequencer_state(void)
{
@@ -11,3 +24,906 @@ void remove_sequencer_state(void)
remove_dir_recursively(&seq_dir, 0);
strbuf_release(&seq_dir);
}
+
+static const char *action_name(const struct replay_opts *opts)
+{
+ return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+}
+
+static char *get_encoding(const char *message);
+
+struct commit_message {
+ char *parent_label;
+ const char *label;
+ const char *subject;
+ char *reencoded_message;
+ const char *message;
+};
+
+static int get_message(struct commit *commit, struct commit_message *out)
+{
+ const char *encoding;
+ const char *abbrev, *subject;
+ int abbrev_len, subject_len;
+ char *q;
+
+ if (!commit->buffer)
+ return -1;
+ encoding = get_encoding(commit->buffer);
+ if (!encoding)
+ encoding = "UTF-8";
+ if (!git_commit_encoding)
+ git_commit_encoding = "UTF-8";
+
+ out->reencoded_message = NULL;
+ out->message = commit->buffer;
+ if (strcmp(encoding, git_commit_encoding))
+ out->reencoded_message = reencode_string(commit->buffer,
+ git_commit_encoding, encoding);
+ if (out->reencoded_message)
+ out->message = out->reencoded_message;
+
+ abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+ abbrev_len = strlen(abbrev);
+
+ subject_len = find_commit_subject(out->message, &subject);
+
+ out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
+ strlen("... ") + subject_len + 1);
+ q = out->parent_label;
+ q = mempcpy(q, "parent of ", strlen("parent of "));
+ out->label = q;
+ q = mempcpy(q, abbrev, abbrev_len);
+ q = mempcpy(q, "... ", strlen("... "));
+ out->subject = q;
+ q = mempcpy(q, subject, subject_len);
+ *q = '\0';
+ return 0;
+}
+
+static void free_message(struct commit_message *msg)
+{
+ free(msg->parent_label);
+ free(msg->reencoded_message);
+}
+
+static char *get_encoding(const char *message)
+{
+ const char *p = message, *eol;
+
+ while (*p && *p != '\n') {
+ for (eol = p + 1; *eol && *eol != '\n'; eol++)
+ ; /* do nothing */
+ if (!prefixcmp(p, "encoding ")) {
+ char *result = xmalloc(eol - 8 - p);
+ strlcpy(result, p + 9, eol - 8 - p);
+ return result;
+ }
+ p = eol;
+ if (*p == '\n')
+ p++;
+ }
+ return NULL;
+}
+
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
+{
+ const char *filename;
+ int fd;
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
+
+ filename = git_path("%s", pseudoref);
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die_errno(_("Could not open '%s' for writing"), filename);
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
+ die_errno(_("Could not write to '%s'"), filename);
+ strbuf_release(&buf);
+}
+
+static void print_advice(int show_hint)
+{
+ char *msg = getenv("GIT_CHERRY_PICK_HELP");
+
+ if (msg) {
+ fprintf(stderr, "%s\n", msg);
+ /*
+ * A conflict has occured but the porcelain
+ * (typically rebase --interactive) wants to take care
+ * of the commit itself so remove CHERRY_PICK_HEAD
+ */
+ unlink(git_path("CHERRY_PICK_HEAD"));
+ return;
+ }
+
+ if (show_hint) {
+ advise("after resolving the conflicts, mark the corrected paths");
+ advise("with 'git add <paths>' or 'git rm <paths>'");
+ advise("and commit the result with 'git commit'");
+ }
+}
+
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno(_("Could not write to %s"), filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die(_("Error wrapping up %s"), filename);
+}
+
+static struct tree *empty_tree(void)
+{
+ return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+}
+
+static int error_dirty_index(struct replay_opts *opts)
+{
+ if (read_cache_unmerged())
+ return error_resolve_conflict(action_name(opts));
+
+ /* Different translation strings for cherry-pick and revert */
+ if (opts->action == REPLAY_PICK)
+ error(_("Your local changes would be overwritten by cherry-pick."));
+ else
+ error(_("Your local changes would be overwritten by revert."));
+
+ if (advice_commit_before_merge)
+ advise(_("Commit your changes or stash them to proceed."));
+ return -1;
+}
+
+static int fast_forward_to(const unsigned char *to, const unsigned char *from)
+{
+ struct ref_lock *ref_lock;
+
+ read_cache();
+ if (checkout_fast_forward(from, to))
+ exit(1); /* the callee should have complained already */
+ ref_lock = lock_any_ref_for_update("HEAD", from, 0);
+ return write_ref_sha1(ref_lock, to, "cherry-pick");
+}
+
+static int do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf,
+ struct replay_opts *opts)
+{
+ struct merge_options o;
+ struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
+ const char **xopt;
+ static struct lock_file index_lock;
+
+ index_fd = hold_locked_index(&index_lock, 1);
+
+ read_cache();
+
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
+
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
+ parse_merge_opt(&o, *xopt);
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
+ die(_("%s: Unable to write new index file"), action_name(opts));
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ }
+
+ return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg, struct replay_opts *opts)
+{
+ /* 6 is max possible length of our args array including NULL */
+ const char *args[6];
+ int i = 0;
+
+ args[i++] = "commit";
+ args[i++] = "-n";
+ if (opts->signoff)
+ args[i++] = "-s";
+ if (!opts->edit) {
+ args[i++] = "-F";
+ args[i++] = defmsg;
+ }
+ args[i] = NULL;
+
+ return run_command_v_opt(args, RUN_GIT_CMD);
+}
+
+static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
+{
+ unsigned char head[20];
+ struct commit *base, *next, *parent;
+ const char *base_label, *next_label;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+ char *defmsg = NULL;
+ struct strbuf msgbuf = STRBUF_INIT;
+ int res;
+
+ if (opts->no_commit) {
+ /*
+ * We do not intend to commit immediately. We just want to
+ * merge the differences in, so let's compute the tree
+ * that represents the "current" state for merge-recursive
+ * to work on.
+ */
+ if (write_cache_as_tree(head, 0, NULL))
+ die (_("Your index file is unmerged."));
+ } else {
+ if (get_sha1("HEAD", head))
+ return error(_("You do not have a valid HEAD"));
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ }
+ discard_cache();
+
+ if (!commit->parents) {
+ parent = NULL;
+ }
+ else if (commit->parents->next) {
+ /* Reverting or cherry-picking a merge commit */
+ int cnt;
+ struct commit_list *p;
+
+ if (!opts->mainline)
+ return error(_("Commit %s is a merge but no -m option was given."),
+ sha1_to_hex(commit->object.sha1));
+
+ for (cnt = 1, p = commit->parents;
+ cnt != opts->mainline && p;
+ cnt++)
+ p = p->next;
+ if (cnt != opts->mainline || !p)
+ return error(_("Commit %s does not have parent %d"),
+ sha1_to_hex(commit->object.sha1), opts->mainline);
+ parent = p->item;
+ } else if (0 < opts->mainline)
+ return error(_("Mainline was specified but commit %s is not a merge."),
+ sha1_to_hex(commit->object.sha1));
+ else
+ parent = commit->parents->item;
+
+ if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
+ return fast_forward_to(commit->object.sha1, head);
+
+ if (parent && parse_commit(parent) < 0)
+ /* TRANSLATORS: The first %s will be "revert" or
+ "cherry-pick", the second %s a SHA1 */
+ return error(_("%s: cannot parse parent commit %s"),
+ action_name(opts), sha1_to_hex(parent->object.sha1));
+
+ if (get_message(commit, &msg) != 0)
+ return error(_("Cannot get commit message for %s"),
+ sha1_to_hex(commit->object.sha1));
+
+ /*
+ * "commit" is an existing commit. We would want to apply
+ * the difference it introduces since its first parent "prev"
+ * on top of the current HEAD if we are cherry-pick. Or the
+ * reverse of it if we are revert.
+ */
+
+ defmsg = git_pathdup("MERGE_MSG");
+
+ if (opts->action == REPLAY_REVERT) {
+ base = commit;
+ base_label = msg.label;
+ next = parent;
+ next_label = msg.parent_label;
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+
+ if (commit->parents && commit->parents->next) {
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
+ }
+ strbuf_addstr(&msgbuf, ".\n");
+ } else {
+ const char *p;
+
+ base = parent;
+ base_label = msg.parent_label;
+ next = commit;
+ next_label = msg.label;
+
+ /*
+ * Append the commit log message to msgbuf; it starts
+ * after the tree, parent, author, committer
+ * information followed by "\n\n".
+ */
+ p = strstr(msg.message, "\n\n");
+ if (p) {
+ p += 2;
+ strbuf_addstr(&msgbuf, p);
+ }
+
+ if (opts->record_origin) {
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
+ }
+ }
+
+ if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
+ res = do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf, opts);
+ write_message(&msgbuf, defmsg);
+ } else {
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
+
+ write_message(&msgbuf, defmsg);
+
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+ common, sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ }
+
+ /*
+ * If the merge was clean or if it failed due to conflict, we write
+ * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
+ * However, if the merge did not even start, then we don't want to
+ * write it at all.
+ */
+ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
+ write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+ if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
+ write_cherry_pick_head(commit, "REVERT_HEAD");
+
+ if (res) {
+ error(opts->action == REPLAY_REVERT
+ ? _("could not revert %s... %s")
+ : _("could not apply %s... %s"),
+ find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+ msg.subject);
+ print_advice(res == 1);
+ rerere(opts->allow_rerere_auto);
+ } else {
+ if (!opts->no_commit)
+ res = run_git_commit(defmsg, opts);
+ }
+
+ free_message(&msg);
+ free(defmsg);
+
+ return res;
+}
+
+static void prepare_revs(struct replay_opts *opts)
+{
+ if (opts->action != REPLAY_REVERT)
+ opts->revs->reverse ^= 1;
+
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+
+ if (!opts->revs->commits)
+ die(_("empty commit set passed"));
+}
+
+static void read_and_refresh_cache(struct replay_opts *opts)
+{
+ static struct lock_file index_lock;
+ int index_fd = hold_locked_index(&index_lock, 0);
+ if (read_index_preload(&the_index, NULL) < 0)
+ die(_("git %s: failed to read the index"), action_name(opts));
+ refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+ if (the_index.cache_changed) {
+ if (write_index(&the_index, index_fd) ||
+ commit_locked_index(&index_lock))
+ die(_("git %s: failed to refresh the index"), action_name(opts));
+ }
+ rollback_lock_file(&index_lock);
+}
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ * struct commit_list *list;
+ * struct commit_list **next = &list;
+ *
+ * next = commit_list_append(c1, next);
+ * next = commit_list_append(c2, next);
+ * assert(commit_list_count(list) == 2);
+ * return list;
+ */
+static struct commit_list **commit_list_append(struct commit *commit,
+ struct commit_list **next)
+{
+ struct commit_list *new = xmalloc(sizeof(struct commit_list));
+ new->item = commit;
+ *next = new;
+ new->next = NULL;
+ return &new->next;
+}
+
+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
+ struct replay_opts *opts)
+{
+ struct commit_list *cur = NULL;
+ const char *sha1_abbrev = NULL;
+ const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
+ const char *subject;
+ int subject_len;
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+ subject_len = find_commit_subject(cur->item->buffer, &subject);
+ strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
+ subject_len, subject);
+ }
+ return 0;
+}
+
+static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
+{
+ unsigned char commit_sha1[20];
+ enum replay_action action;
+ char *end_of_object_name;
+ int saved, status, padding;
+
+ if (!prefixcmp(bol, "pick")) {
+ action = REPLAY_PICK;
+ bol += strlen("pick");
+ } else if (!prefixcmp(bol, "revert")) {
+ action = REPLAY_REVERT;
+ bol += strlen("revert");
+ } else
+ return NULL;
+
+ /* Eat up extra spaces/ tabs before object name */
+ padding = strspn(bol, " \t");
+ if (!padding)
+ return NULL;
+ bol += padding;
+
+ end_of_object_name = bol + strcspn(bol, " \t\n");
+ saved = *end_of_object_name;
+ *end_of_object_name = '\0';
+ status = get_sha1(bol, commit_sha1);
+ *end_of_object_name = saved;
+
+ /*
+ * Verify that the action matches up with the one in
+ * opts; we don't support arbitrary instructions
+ */
+ if (action != opts->action) {
+ const char *action_str;
+ action_str = action == REPLAY_REVERT ? "revert" : "cherry-pick";
+ error(_("Cannot %s during a %s"), action_str, action_name(opts));
+ return NULL;
+ }
+
+ if (status < 0)
+ return NULL;
+
+ return lookup_commit_reference(commit_sha1);
+}
+
+static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ struct commit_list **next = todo_list;
+ struct commit *commit;
+ char *p = buf;
+ int i;
+
+ for (i = 1; *p; i++) {
+ char *eol = strchrnul(p, '\n');
+ commit = parse_insn_line(p, eol, opts);
+ if (!commit)
+ return error(_("Could not parse line %d."), i);
+ next = commit_list_append(commit, next);
+ p = *eol ? eol + 1 : eol;
+ }
+ if (!*todo_list)
+ return error(_("No commits parsed."));
+ return 0;
+}
+
+static void read_populate_todo(struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ struct strbuf buf = STRBUF_INIT;
+ int fd, res;
+
+ fd = open(todo_file, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("Could not open %s"), todo_file);
+ if (strbuf_read(&buf, fd, 0) < 0) {
+ close(fd);
+ strbuf_release(&buf);
+ die(_("Could not read %s."), todo_file);
+ }
+ close(fd);
+
+ res = parse_insn_buffer(buf.buf, todo_list, opts);
+ strbuf_release(&buf);
+ if (res)
+ die(_("Unusable instruction sheet: %s"), todo_file);
+}
+
+static int populate_opts_cb(const char *key, const char *value, void *data)
+{
+ struct replay_opts *opts = data;
+ int error_flag = 1;
+
+ if (!value)
+ error_flag = 0;
+ else if (!strcmp(key, "options.no-commit"))
+ opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.edit"))
+ opts->edit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.signoff"))
+ opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.record-origin"))
+ opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.allow-ff"))
+ opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.mainline"))
+ opts->mainline = git_config_int(key, value);
+ else if (!strcmp(key, "options.strategy"))
+ git_config_string(&opts->strategy, key, value);
+ else if (!strcmp(key, "options.strategy-option")) {
+ ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+ opts->xopts[opts->xopts_nr++] = xstrdup(value);
+ } else
+ return error(_("Invalid key: %s"), key);
+
+ if (!error_flag)
+ return error(_("Invalid value for %s: %s"), key, value);
+
+ return 0;
+}
+
+static void read_populate_opts(struct replay_opts **opts_ptr)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (!file_exists(opts_file))
+ return;
+ if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+ die(_("Malformed options sheet: %s"), opts_file);
+}
+
+static void walk_revs_populate_todo(struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ struct commit *commit;
+ struct commit_list **next;
+
+ prepare_revs(opts);
+
+ next = todo_list;
+ while ((commit = get_revision(opts->revs)))
+ next = commit_list_append(commit, next);
+}
+
+static int create_seq_dir(void)
+{
+ const char *seq_dir = git_path(SEQ_DIR);
+
+ if (file_exists(seq_dir)) {
+ error(_("a cherry-pick or revert is already in progress"));
+ advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
+ return -1;
+ }
+ else if (mkdir(seq_dir, 0777) < 0)
+ die_errno(_("Could not create sequencer directory %s"), seq_dir);
+ return 0;
+}
+
+static void save_head(const char *head)
+{
+ const char *head_file = git_path(SEQ_HEAD_FILE);
+ static struct lock_file head_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+ strbuf_addf(&buf, "%s\n", head);
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ die_errno(_("Could not write to %s"), head_file);
+ if (commit_lock_file(&head_lock) < 0)
+ die(_("Error wrapping up %s."), head_file);
+}
+
+static int reset_for_rollback(const unsigned char *sha1)
+{
+ const char *argv[4]; /* reset --merge <arg> + NULL */
+ argv[0] = "reset";
+ argv[1] = "--merge";
+ argv[2] = sha1_to_hex(sha1);
+ argv[3] = NULL;
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+ unsigned char head_sha1[20];
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ return error(_("cannot resolve HEAD"));
+ if (is_null_sha1(head_sha1))
+ return error(_("cannot abort from a branch yet to be born"));
+ return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+ const char *filename;
+ FILE *f;
+ unsigned char sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+
+ filename = git_path(SEQ_HEAD_FILE);
+ f = fopen(filename, "r");
+ if (!f && errno == ENOENT) {
+ /*
+ * There is no multiple-cherry-pick in progress.
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+ * a single-cherry-pick in progress, abort that.
+ */
+ return rollback_single_pick();
+ }
+ if (!f)
+ return error(_("cannot open %s: %s"), filename,
+ strerror(errno));
+ if (strbuf_getline(&buf, f, '\n')) {
+ error(_("cannot read %s: %s"), filename, ferror(f) ?
+ strerror(errno) : _("unexpected end of file"));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+ filename);
+ goto fail;
+ }
+ if (reset_for_rollback(sha1))
+ goto fail;
+ remove_sequencer_state();
+ strbuf_release(&buf);
+ return 0;
+fail:
+ strbuf_release(&buf);
+ return -1;
+}
+
+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ static struct lock_file todo_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+ if (format_todo(&buf, todo_list, opts) < 0)
+ die(_("Could not format %s."), todo_file);
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
+ strbuf_release(&buf);
+ die_errno(_("Could not write to %s"), todo_file);
+ }
+ if (commit_lock_file(&todo_lock) < 0) {
+ strbuf_release(&buf);
+ die(_("Error wrapping up %s."), todo_file);
+ }
+ strbuf_release(&buf);
+}
+
+static void save_opts(struct replay_opts *opts)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (opts->no_commit)
+ git_config_set_in_file(opts_file, "options.no-commit", "true");
+ if (opts->edit)
+ git_config_set_in_file(opts_file, "options.edit", "true");
+ if (opts->signoff)
+ git_config_set_in_file(opts_file, "options.signoff", "true");
+ if (opts->record_origin)
+ git_config_set_in_file(opts_file, "options.record-origin", "true");
+ if (opts->allow_ff)
+ git_config_set_in_file(opts_file, "options.allow-ff", "true");
+ if (opts->mainline) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "%d", opts->mainline);
+ git_config_set_in_file(opts_file, "options.mainline", buf.buf);
+ strbuf_release(&buf);
+ }
+ if (opts->strategy)
+ git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+ if (opts->xopts) {
+ int i;
+ for (i = 0; i < opts->xopts_nr; i++)
+ git_config_set_multivar_in_file(opts_file,
+ "options.strategy-option",
+ opts->xopts[i], "^$", 0);
+ }
+}
+
+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
+{
+ struct commit_list *cur;
+ int res;
+
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ if (opts->allow_ff)
+ assert(!(opts->signoff || opts->no_commit ||
+ opts->record_origin || opts->edit));
+ read_and_refresh_cache(opts);
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ save_todo(cur, opts);
+ res = do_pick_commit(cur->item, opts);
+ if (res)
+ return res;
+ }
+
+ /*
+ * Sequence of picks finished successfully; cleanup by
+ * removing the .git/sequencer directory
+ */
+ remove_sequencer_state();
+ return 0;
+}
+
+static int continue_single_pick(void)
+{
+ const char *argv[] = { "commit", NULL };
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int sequencer_continue(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ return continue_single_pick();
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list, opts);
+
+ /* Verify that the conflict has been resolved */
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
+ return pick_commits(todo_list, opts);
+}
+
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ return do_pick_commit(cmit, opts);
+}
+
+int sequencer_pick_revisions(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+ unsigned char sha1[20];
+
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
+ read_and_refresh_cache(opts);
+
+ /*
+ * Decide what to do depending on the arguments; a fresh
+ * cherry-pick should be handled differently from an existing
+ * one that is being continued
+ */
+ if (opts->subcommand == REPLAY_REMOVE_STATE) {
+ remove_sequencer_state();
+ return 0;
+ }
+ if (opts->subcommand == REPLAY_ROLLBACK)
+ return sequencer_rollback(opts);
+ if (opts->subcommand == REPLAY_CONTINUE)
+ return sequencer_continue(opts);
+
+ /*
+ * If we were called as "git cherry-pick <commit>", just
+ * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+ * REVERT_HEAD, and don't touch the sequencer state.
+ * This means it is possible to cherry-pick in the middle
+ * of a cherry-pick sequence.
+ */
+ if (opts->revs->cmdline.nr == 1 &&
+ opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+ opts->revs->no_walk &&
+ !opts->revs->cmdline.rev->flags) {
+ struct commit *cmit;
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+ cmit = get_revision(opts->revs);
+ if (!cmit || get_revision(opts->revs))
+ die("BUG: expected exactly one commit from walk");
+ return single_pick(cmit, opts);
+ }
+
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
+
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0)
+ return -1;
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REPLAY_REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
+ }
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
+ return pick_commits(todo_list, opts);
+}
diff --git a/sequencer.h b/sequencer.h
index 2d4528f..bb4b138 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -6,7 +6,44 @@
#define SEQ_TODO_FILE "sequencer/todo"
#define SEQ_OPTS_FILE "sequencer/opts"
+enum replay_action {
+ REPLAY_REVERT,
+ REPLAY_PICK
+};
+
+enum replay_subcommand {
+ REPLAY_NONE,
+ REPLAY_REMOVE_STATE,
+ REPLAY_CONTINUE,
+ REPLAY_ROLLBACK
+};
+
+struct replay_opts {
+ enum replay_action action;
+ enum replay_subcommand subcommand;
+
+ /* Boolean options */
+ int edit;
+ int record_origin;
+ int no_commit;
+ int signoff;
+ int allow_ff;
+ int allow_rerere_auto;
+
+ int mainline;
+
+ /* Merge strategy */
+ const char *strategy;
+ const char **xopts;
+ size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
+};
+
/* Removes SEQ_DIR. */
extern void remove_sequencer_state(void);
+int sequencer_pick_revisions(struct replay_opts *opts);
+
#endif
--
1.7.8.2
^ permalink raw reply related
* [PATCH 1/2] revert: prepare to move replay_action to header
From: Ramkumar Ramachandra @ 2012-01-11 18:15 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder
In-Reply-To: <1326305757-27525-1-git-send-email-artagnon@gmail.com>
REVERT and CHERRY_PICK and are unsuitable names for an enumerator in a
public interface, because they are generic enough to be likely to
clash with identifiers with other meanings. Rename to REPLAY_REVERT
and REPLAY_PICK as preparation for exposing them.
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
builtin/revert.c | 40 ++++++++++++++++++++++------------------
1 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 0d8020c..2739405 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -39,7 +39,11 @@ static const char * const cherry_pick_usage[] = {
NULL
};
-enum replay_action { REVERT, CHERRY_PICK };
+enum replay_action {
+ REPLAY_REVERT,
+ REPLAY_PICK
+};
+
enum replay_subcommand {
REPLAY_NONE,
REPLAY_REMOVE_STATE,
@@ -74,14 +78,14 @@ struct replay_opts {
static const char *action_name(const struct replay_opts *opts)
{
- return opts->action == REVERT ? "revert" : "cherry-pick";
+ return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
}
static char *get_encoding(const char *message);
static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
{
- return opts->action == REVERT ? revert_usage : cherry_pick_usage;
+ return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
}
static int option_parse_x(const struct option *opt,
@@ -160,7 +164,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_END(),
};
- if (opts->action == CHERRY_PICK) {
+ if (opts->action == REPLAY_PICK) {
struct option cp_extra[] = {
OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
@@ -374,7 +378,7 @@ static int error_dirty_index(struct replay_opts *opts)
return error_resolve_conflict(action_name(opts));
/* Different translation strings for cherry-pick and revert */
- if (opts->action == CHERRY_PICK)
+ if (opts->action == REPLAY_PICK)
error(_("Your local changes would be overwritten by cherry-pick."));
else
error(_("Your local changes would be overwritten by revert."));
@@ -553,7 +557,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
defmsg = git_pathdup("MERGE_MSG");
- if (opts->action == REVERT) {
+ if (opts->action == REPLAY_REVERT) {
base = commit;
base_label = msg.label;
next = parent;
@@ -594,7 +598,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
}
}
- if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) {
+ if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
head, &msgbuf, opts);
write_message(&msgbuf, defmsg);
@@ -618,13 +622,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
* However, if the merge did not even start, then we don't want to
* write it at all.
*/
- if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
+ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
- if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
+ if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
write_cherry_pick_head(commit, "REVERT_HEAD");
if (res) {
- error(opts->action == REVERT
+ error(opts->action == REPLAY_REVERT
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
@@ -644,7 +648,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
static void prepare_revs(struct replay_opts *opts)
{
- if (opts->action != REVERT)
+ if (opts->action != REPLAY_REVERT)
opts->revs->reverse ^= 1;
if (prepare_revision_walk(opts->revs))
@@ -701,7 +705,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
{
struct commit_list *cur = NULL;
const char *sha1_abbrev = NULL;
- const char *action_str = opts->action == REVERT ? "revert" : "pick";
+ const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
const char *subject;
int subject_len;
@@ -722,10 +726,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *
int saved, status, padding;
if (!prefixcmp(bol, "pick")) {
- action = CHERRY_PICK;
+ action = REPLAY_PICK;
bol += strlen("pick");
} else if (!prefixcmp(bol, "revert")) {
- action = REVERT;
+ action = REPLAY_REVERT;
bol += strlen("revert");
} else
return NULL;
@@ -748,7 +752,7 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *
*/
if (action != opts->action) {
const char *action_str;
- action_str = action == REVERT ? "revert" : "cherry-pick";
+ action_str = action == REPLAY_REVERT ? "revert" : "cherry-pick";
error(_("Cannot %s during a %s"), action_str, action_name(opts));
return NULL;
}
@@ -1124,7 +1128,7 @@ static int pick_revisions(struct replay_opts *opts)
if (create_seq_dir() < 0)
return -1;
if (get_sha1("HEAD", sha1)) {
- if (opts->action == REVERT)
+ if (opts->action == REPLAY_REVERT)
return error(_("Can't revert as initial commit"));
return error(_("Can't cherry-pick into empty head"));
}
@@ -1141,7 +1145,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
memset(&opts, 0, sizeof(opts));
if (isatty(0))
opts.edit = 1;
- opts.action = REVERT;
+ opts.action = REPLAY_REVERT;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
res = pick_revisions(&opts);
@@ -1156,7 +1160,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
int res;
memset(&opts, 0, sizeof(opts));
- opts.action = CHERRY_PICK;
+ opts.action = REPLAY_PICK;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
res = pick_revisions(&opts);
--
1.7.8.2
^ permalink raw reply related
* [PATCH v3 0/2] The move to sequencer.c
From: Ramkumar Ramachandra @ 2012-01-11 18:15 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder
In-Reply-To: <20120111164758.GD1891@burratino>
Hi,
I figured I'd just post a minimal series performing the move:
1. I can write up scratch code and toy with various ideas I have on
the generalization. It'll help with my thought process.
2. I can stop thinking about how to begin every commit message with a
note about anticipating this move. This'll be a big relief!
Thanks.
Ramkumar Ramachandra (2):
revert: prepare to move replay_action to header
sequencer: factor code out of revert builtin
builtin/revert.c | 952 +-----------------------------------------------------
sequencer.c | 918 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
sequencer.h | 37 +++
3 files changed, 961 insertions(+), 946 deletions(-)
--
1.7.8.2
^ permalink raw reply
* [BUG] multi-commit cherry-pick messes up the order of commits
From: SZEDER Gábor @ 2012-01-11 17:31 UTC (permalink / raw)
To: Christian Couder, git
Hi,
I did some multi-commit cherry-picks lately, and noticed that
sometimes cherry-pick applied the commits in different order than I
specified on the command line. After some debugging, today I could
finally come up with a receipe to reproduce:
git init
echo 1 >a && git add a
git commit -m a
echo 2 >b && git add b
git commit -m b
sleep 2 && echo 3 >c && git add c
git commit -m c
git checkout -b branch HEAD^^
git cherry-pick master master^ # the later commit first
where the 'git cherry-pick' command produces the following output:
[branch ef5b86e0] b
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 b
[branch 6a74f934] c
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 c
Notice that master^, i.e. the commit adding the file 'b', is picked
before master, i.e. the commit adding 'c', although the order on the
command line was the reverse.
This is because
cmd_cherry_pick()
pick_revisions()
walk_revs_populate_todo()
prepare_revs()
calls prepare_revision_walk(), which parses the commits from the
command line in the order they were specified, but inserts them into a
list ordered by date, and commits will be picked in the order they
appear in this list. So if you specify commits in a different order
than their committer date or commits with the same commiter date
(which are often produced by am, rebase, and multi-commit
cherry-pick), then they will be picked in wrong order.
As far as I can tell, this buggy behavior is as old as multi-commit
cherry-pick itself, i.e. 7e2bfd3f (revert: allow cherry-picking more
than one commit, 2010-06-02).
Best,
Gábor
^ permalink raw reply
* [PATCH 2/2] diff --word-diff: use non-whitespace regex by default
From: Tay Ray Chuan @ 2012-01-11 17:25 UTC (permalink / raw)
To: Git Mailing List; +Cc: Junio C Hamano
In-Reply-To: <1326302702-4536-1-git-send-email-rctay89@gmail.com>
Factor out the comprehensive non-whitespace regex in use by PATTERNS and
IPATTERN and use it as the word-diff regex for the default diff driver.
As the default regex is no longer non-empty, update the word-regex
selection logic (non-default driver from pre-image, then post-image,
then the diff.wordRegex config) accordingly.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
diff.c | 14 ++++++++------
t/t4034-diff-words.sh | 31 +++++++++----------------------
userdiff.c | 8 +++++---
3 files changed, 22 insertions(+), 31 deletions(-)
diff --git a/diff.c b/diff.c
index 374ecf3..5f71f9f 100644
--- a/diff.c
+++ b/diff.c
@@ -1987,9 +1987,10 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe
return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
}
-static const char *userdiff_word_regex(struct diff_filespec *one)
+static const char *userdiff_word_regex(struct diff_filespec *one, int *is_default)
{
diff_filespec_load_driver(one);
+ *is_default = !strcmp(one->driver->name, "default");
return one->driver->word_regex;
}
@@ -2180,17 +2181,18 @@ static void builtin_diff(const char *name_a,
else if (!prefixcmp(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
if (o->word_diff) {
- int i;
+ int i, is_default;
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->type = o->word_diff;
ecbdata.diff_words->opt = o;
+ is_default = 0;
if (!o->word_regex)
- o->word_regex = userdiff_word_regex(one);
- if (!o->word_regex)
- o->word_regex = userdiff_word_regex(two);
- if (!o->word_regex)
+ o->word_regex = userdiff_word_regex(one, &is_default);
+ if (is_default)
+ o->word_regex = userdiff_word_regex(two, &is_default);
+ if (is_default && diff_word_regex_cfg)
o->word_regex = diff_word_regex_cfg;
if (o->word_regex) {
ecbdata.diff_words->word_regex = (regex_t *)
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 9ae0e1a..e588849 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -84,26 +84,13 @@ test_expect_success setup '
git config diff.color.func magenta
'
-test_expect_success 'set up pre and post with runs of whitespace' '
+test_expect_success 'set up pre and post with runs of non-whitespace' '
cp pre.simple pre &&
- cp post.simple post
+ cp post.simple post &&
+ cp expect.non-whitespace-is-word expect
'
-test_expect_success 'word diff with runs of whitespace' '
- cat >expect <<-\EOF &&
- <BOLD>diff --git a/pre b/post<RESET>
- <BOLD>index 330b04f..5ed8eff 100644<RESET>
- <BOLD>--- a/pre<RESET>
- <BOLD>+++ b/post<RESET>
- <CYAN>@@ -1,3 +1,7 @@<RESET>
- <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
-
- a = b + c<RESET>
-
- <GREEN>aa = a<RESET>
-
- <GREEN>aeff = aeff * ( aaa )<RESET>
- EOF
+test_expect_success 'word diff defaults to runs of non-whitespace' '
word_diff --color-words &&
word_diff --word-diff=color &&
word_diff --color --word-diff=color
@@ -116,8 +103,8 @@ test_expect_success '--word-diff=porcelain' '
--- a/pre
+++ b/post
@@ -1,3 +1,7 @@
- -h(4)
- +h(4),hh[44]
+ h(4)
+ +,hh[44]
~
# significant space
~
@@ -140,7 +127,7 @@ test_expect_success '--word-diff=plain' '
--- a/pre
+++ b/post
@@ -1,3 +1,7 @@
- [-h(4)-]{+h(4),hh[44]+}
+ h(4){+,hh[44]+}
a = b + c
@@ -159,7 +146,7 @@ test_expect_success '--word-diff=plain --color' '
<BOLD>--- a/pre<RESET>
<BOLD>+++ b/post<RESET>
<CYAN>@@ -1,3 +1,7 @@<RESET>
- <RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
+ h(4)<GREEN>{+,hh[44]+}<RESET>
a = b + c<RESET>
@@ -177,7 +164,7 @@ test_expect_success 'word diff without context' '
<BOLD>--- a/pre<RESET>
<BOLD>+++ b/post<RESET>
<CYAN>@@ -1 +1 @@<RESET>
- <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+ h(4)<GREEN>,hh[44]<RESET>
<CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
<GREEN>aa = a<RESET>
diff --git a/userdiff.c b/userdiff.c
index 76109da..cf38566 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -7,12 +7,14 @@ static struct userdiff_driver *drivers;
static int ndrivers;
static int drivers_alloc;
+#define NON_WHITESPACE \
+ "[^[:space:]]|[\xc0-\xff][\x80-\xbf]+"
#define PATTERNS(name, pattern, word_regex) \
{ name, NULL, -1, { pattern, REG_EXTENDED }, \
- word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
+ word_regex "|" NON_WHITESPACE }
#define IPATTERN(name, pattern, word_regex) \
{ name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, \
- word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
+ word_regex "|" NON_WHITESPACE }
static struct userdiff_driver builtin_drivers[] = {
IPATTERN("fortran",
"!^([C*]|[ \t]*!)\n"
@@ -140,7 +142,7 @@ PATTERNS("csharp",
"[a-zA-Z_][a-zA-Z0-9_]*"
"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
"|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-{ "default", NULL, -1, { NULL, 0 } },
+{ "default", NULL, -1, { NULL, 0 }, NON_WHITESPACE },
};
#undef PATTERNS
#undef IPATTERN
--
1.7.7.584.g16d0ea
^ permalink raw reply related
* [PATCH 1/2] t4034-diff-words: replace regex for diff driver
From: Tay Ray Chuan @ 2012-01-11 17:25 UTC (permalink / raw)
To: Git Mailing List; +Cc: Junio C Hamano
The next patch uses a non-whitespace regex, similar to the regex
currently used by the 'testdriver' diff driver; replace the regex with a
distinct one so that we can continue to conclude its effects.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
Kept separate to keep the next patch clean.
---
t/t4034-diff-words.sh | 20 +++++++++++++++++---
1 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 6f1e5a2..9ae0e1a 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -46,6 +46,20 @@ cat >expect.non-whitespace-is-word <<-\EOF
<GREEN>aeff = aeff * ( aaa )<RESET>
EOF
+cat >expect.everything-is-word <<-\EOF
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+
+ a = b + c<RESET>
+
+ <GREEN>aa = a<RESET>
+
+ <GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
word_diff () {
test_must_fail git diff --no-index "$@" pre post >output &&
@@ -179,7 +193,7 @@ test_expect_success 'word diff with a regular expression' '
'
test_expect_success 'set up a diff driver' '
- git config diff.testdriver.wordRegex "[^[:space:]]" &&
+ git config diff.testdriver.wordRegex ".+" &&
cat <<-\EOF >.gitattributes
pre diff=testdriver
post diff=testdriver
@@ -192,7 +206,7 @@ test_expect_success 'option overrides .gitattributes' '
'
test_expect_success 'use regex supplied by driver' '
- cp expect.non-whitespace-is-word expect &&
+ cp expect.everything-is-word expect &&
word_diff --color-words
'
@@ -224,7 +238,7 @@ test_expect_success 'command-line overrides config: --word-diff-regex' '
'
test_expect_success '.gitattributes override config' '
- cp expect.non-whitespace-is-word expect &&
+ cp expect.everything-is-word expect &&
word_diff --color-words
'
--
1.7.7.584.g16d0ea
^ permalink raw reply related
* why git-svn dcommit recreates master branch?
From: Pawel Sikora @ 2012-01-11 16:27 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 1218 bytes --]
Hi,
i'm wondering why the git-svn-dcommit recreates a master branch in my repo.
is it a bug or feature and master is required to cooperate with subversion?
please look at attached test.sh:
$ ./test.sh
Checked out revision 0.
A file.txt
Adding file.txt
Transmitting file data .
Committed revision 1.
Initialized empty Git repository in /home/users/pawels/bugs/git-svn-master-issue/repo.git/.git/
A file.txt
r1 = 464dbae76d675c8c4bdc715e34ebe46691b8bfa2 (refs/remotes/git-svn)
Checked out HEAD:
file:///home/users/pawels/bugs/git-svn-master-issue/repo.svn r1
branches before dcommit:
* bridge
remotes/git-svn
[bridge ffffc26] bar
1 files changed, 1 insertions(+), 0 deletions(-)
Committing to file:///home/users/pawels/bugs/git-svn-master-issue/repo.svn ...
M file.txt
Committed r2
M file.txt
r2 = d98723bccdcf673790b55d47a4f345c694a68ced (refs/remotes/git-svn)
No changes between current HEAD and refs/remotes/git-svn
Resetting to the latest refs/remotes/git-svn
branches after dcommit:
* bridge
master
remotes/git-svn
could some one put some light on this?
BR,
Paweł.
please CC me on reply.
[-- Attachment #2: test.sh --]
[-- Type: application/x-shellscript, Size: 489 bytes --]
^ permalink raw reply
* Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands
From: Ramkumar Ramachandra @ 2012-01-11 16:52 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Git List, Junio C Hamano
In-Reply-To: <20120111164758.GD1891@burratino>
Jonathan Nieder wrote:
> In that case, why not just a patch to move the code to sequencer.c,
> with whatever is the minimum of related fixes (just namespace stuff,
> I'd imagine) before it?
Cool, I'll do that then. I thought what I'd originally posted was an
acceptable minimum to show the motivation for the move.
-- Ram
^ permalink raw reply
* Re: rsync a *bunch* of git repos
From: Neal Kreitzinger @ 2012-01-11 16:52 UTC (permalink / raw)
To: Sergio; +Cc: git
In-Reply-To: <loom.20120111T141805-791@post.gmane.org>
On 1/11/2012 7:22 AM, Sergio wrote:
>
> As an aside: git works fine when repos are transferred with rsync, but git packs
> are not rsync friendly nor friendly with backup strategies using binary deltas.
>
All these answers are assuming that your source git repos are quiet
during the rsync. If they are not quiet during the rsync then you are
going to need a more tailored rsync to do things in a certain order.
See this thread for details:
http://thread.gmane.org/gmane.comp.version-control.git/168699
v/r,
neal
^ permalink raw reply
* Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands
From: Jonathan Nieder @ 2012-01-11 16:47 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Git List, Junio C Hamano
In-Reply-To: <CALkWK0=gvsvqk7Th7YY_eRzb+Ri52AZbOVokC98i9BXVAJOZEw@mail.gmail.com>
Ramkumar Ramachandra wrote:
> More than the pain of rebasing the patch everytime, I guess what I'm
> asking is: is it worth stretching my foresight like this? Once the
> code is in sequencer.c, it just becomes so much easier for me to write
> scratch code to help me wrap my head around the generalization.
In that case, why not just a patch to move the code to sequencer.c,
with whatever is the minimum of related fixes (just namespace stuff,
I'd imagine) before it?
^ permalink raw reply
* Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands
From: Ramkumar Ramachandra @ 2012-01-11 16:39 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Git List, Junio C Hamano
In-Reply-To: <20120111131854.GG32173@burratino>
Jonathan Nieder wrote:
> Honestly, moving code verbatim between files is very easy. Repeatedly
> rebasing a patch that carries out such a move would presumably be
> hard, though. But this pain is unnecessary!
>
> Just like I haven't been reviewing the code movement, I'd be perfectly
> happy to read a "patch" that says
>
> "And then we move the functions from the following list to
> sequencer.c. I'll send a patch doing so once work has settled
> down in patches earlier in this series."
More than the pain of rebasing the patch everytime, I guess what I'm
asking is: is it worth stretching my foresight like this? Once the
code is in sequencer.c, it just becomes so much easier for me to write
scratch code to help me wrap my head around the generalization. If
the answer to the question is yes, I suppose it makes sense to submit
the good parts now and work on the other parts over an extended period
of time.
-- Ram
^ permalink raw reply
* Re: Re* Regulator updates for 3.3
From: Linus Torvalds @ 2012-01-11 16:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Mark Brown, Liam Girdwood, linux-kernel, Git Mailing List
In-Reply-To: <7vzkdu7miv.fsf@alter.siamese.dyndns.org>
On Tue, Jan 10, 2012 at 10:59 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> What makes me uneasy about the idea of running the editor by default is
> that many people still use Git as a better CVS/SVN. Their workflow is to
> build randomly on their 'master', attempt to push and get rejected, pull
> only so that they can push out, and then push the merge result out.
Sure. And I don't think we can do much about it. They'll either set
the legacy flag, or they'll just exit the editor without adding
anything useful (if you come from a CVS background in particular, you
probably never learnt to do good commit logs anyway).
So it will be a bit more work for the bad workflow, I agree - although
if it really irritates people, they can just set that GIT_MERGE_LEGACY
in their .bashrc files or something. But we can *hope* that even those
people might sometimes actually talk about what/why they are doing
things, or maybe even learn about that whole "distributed" thing.
I agree that is unlikely to ever happen, though. It's more likely that
they will change their aliases so that their "update" command just
adds the --no-edit flag. Regardless, it doesn't sound *too* onerous to
work around.
Patch looks good to me. I would personally have compared "st_mode"
instead of (or in addition to) "st_rdev", but I don't think it matters
all that much.
Linus
^ permalink raw reply
* Re: Re* Regulator updates for 3.3
From: Phil Hord @ 2012-01-11 16:14 UTC (permalink / raw)
To: Junio C Hamano
Cc: Linus Torvalds, Mark Brown, Liam Girdwood, Git Mailing List
In-Reply-To: <7vzkdu7miv.fsf@alter.siamese.dyndns.org>
On Wed, Jan 11, 2012 at 1:59 AM, Junio C Hamano <gitster@pobox.com> wrote:
[...]
>
> With that caveat, the patch should look like this.
>
> -- >8 --
> Subject: [PATCH] merge: use editor by default in interactive sessions
>
> Traditionally, a cleanly resolved merge was committed by "git merge" using
> the auto-generated merge commit log message with invoking the editor.
>
> After 5 years of use in the field, it turns out that many people perform
> too many unjustified backmerges of the upstream history into their topic
> branches. These merges are not just useless, but they are more often than
> not explained and making the end result unreadable when it gets time for
> merging their history back to their upstream.
Typo, I think. I believe you meant "they are more often than not not
explained", but as this is unclear, maybe you can use "they are
usually not explained" or "they more often than not go in without
explanation".
P
^ permalink raw reply
* Re: git svn dcommit sends to wrong branch
From: Thomas Rast @ 2012-01-11 15:31 UTC (permalink / raw)
To: git; +Cc: Victor Engmark
In-Reply-To: <20120111140513.GA12633@victor>
Victor Engmark <victor.engmark@terreactive.ch> writes:
> This message was never delivered and no error message ever came back; is
> there some weird filtering going on?
It was delivered, see e.g.
http://thread.gmane.org/gmane.comp.version-control.git/188265
However, my reply ended up not having a Cc to you because your
Mail-Followup-To header fooled Gnus into believing you didn't want that
to happen. Please do not set this header; we Cc everyone involved in
discussions so far, and MFT makes it that much less convenient to
achieve that.
Since you are apparently not subscribed (otherwise you should have
received my reply), please find a cut&paste of the original reply below.
---- 8< ----
Victor Engmark <victor.engmark@terreactive.ch> writes:
> Commands:
>
> git svn clone -s -r 1:HEAD http://svn/repo
> cd repo
> git commit [thrice, in master only]
Which git version is this? Before 1.6.5 (b186a261 to be precise)
git-svn pointed master at the branch where the last commit in SVN
happened, which is not necessarily trunk. After that it tries to point
it at trunk instead. You can find out, e.g., by saying 'git show' on
the fresh clone and looking at the git-svn-id line.
> git rebase --interactive HEAD~20 [i.e., started rebase in commits before
> the clone]
> [Merged two commits I had made *after* the clone]
> git commit ...
> git dcommit
>
> This created commits on
> <http://svn/repo/branches/branch_name>! Why? Is it because HEAD~20's
> git-svn-id <http://svn/repo/branches/branch_name@22481> is on that
> branch?
The rule is that the commits go to the branch named in the git-svn-id
line of the most recent first-parent ancestor of HEAD.
You can find the "base" commit in question with
git log -1 --first-parent --grep=^git-svn-id:
> And more importantly, how do I "replay" my commits on trunk?
You need to rebase the commits on trunk, and (very important) strip the
git-svn-id lines from their messages. If you only had a handful of
commits, your best bet is to use something like
git checkout -b newbranch
git rebase -i --onto svn/trunk svn/branch_name # or whatever git-svn named the remote branches
# edit all the 'pick' into 'reword'
# in every commit message editor that pops up, remove the git-svn-id line
gitk # make sure that you like the resulting history!
git svn dcommit
(If you have many commits, git-filter-branch can do the removal
automatically, but it's a bit of a loaded gun pointed at your foot.)
If your git-rebase is too old for 'reword', you can use 'edit' instead
and then, every time that git-rebase drops you into a command line, say
git commit --amend # and edit the commit message
git rebase --continue
--
Thomas Rast
trast@{inf,student}.ethz.ch
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox