* [PATCH] built-in diff.
@ 2006-04-29 7:19 Junio C Hamano
2006-04-29 9:29 ` [PATCH] built-in diff: assorted updates Junio C Hamano
2006-04-29 9:45 ` [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents" Junio C Hamano
0 siblings, 2 replies; 5+ messages in thread
From: Junio C Hamano @ 2006-04-29 7:19 UTC (permalink / raw)
To: git
This starts to replace the shell script version of "git diff".
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
* Should apply on top of "next" with trivial conflict fixups
(hint: "git am -3").
It is not fully compatible with the shell script version yet,
so it is called "git diffn" for now. It does not do the
fancy option defaulting, other than "git diffn --cached" to
mean "git diffn --cached HEAD", and I do not think I got the
defaulting to --cc right either. Also it does not default to
do -M either. If somebody cares deeply, patches are welcome.
When its option defaulting gets compatible with the shell
script version, or when its new default options becomes
accepted, we will rename it to "git diff" and merge it in.
To ask for the diff-raw output, you can give --raw option.
Makefile | 2
builtin-diff.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
builtin.h | 1
git.c | 1
4 files changed, 335 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
index 8ce27a6..277c1ac 100644
--- a/Makefile
+++ b/Makefile
@@ -214,7 +214,7 @@ LIB_OBJS = \
$(DIFF_OBJS)
BUILTIN_OBJS = \
- builtin-log.o builtin-help.o
+ builtin-log.o builtin-help.o builtin-diff.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
diff --git a/builtin-diff.c b/builtin-diff.c
new file mode 100644
index 0000000..1968212
--- /dev/null
+++ b/builtin-diff.c
@@ -0,0 +1,332 @@
+/*
+ * Builtin "git diff"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+
+/* NEEDSWORK: struct object has place for name but we _do_
+ * know mode when we extracted the blob out of a tree, which
+ * we currently lose.
+ */
+struct blobinfo {
+ unsigned char sha1[20];
+ const char *name;
+};
+
+static const char builtin_diff_usage[] =
+"diff <options> <rev>{0,2} -- <path>*";
+
+static int builtin_diff_files(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int silent = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--base"))
+ revs->max_count = 1;
+ else if (!strcmp(arg, "--ours"))
+ revs->max_count = 2;
+ else if (!strcmp(arg, "--theirs"))
+ revs->max_count = 3;
+ else if (!strcmp(arg, "-q"))
+ silent = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there are NO revision (i.e. pending object) parameter,
+ * rev.max_count is reasonable (0 <= n <= 3),
+ * there is no other revision filtering parameters.
+ */
+ if (revs->pending_objects ||
+ revs->min_age != -1 ||
+ revs->max_age != -1)
+ usage(builtin_diff_usage);
+ /*
+ * Backward compatibility wart - "diff-files -s" used to
+ * defeat the common diff option "-s" which asked for
+ * DIFF_FORMAT_NO_OUTPUT.
+ */
+ if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ return run_diff_files(revs, silent);
+}
+
+static void stuff_change(struct diff_options *opt,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *old_name,
+ const char *new_name)
+{
+ struct diff_filespec *one, *two;
+
+ if (memcmp(null_sha1, old_sha1, 20) &&
+ memcmp(null_sha1, new_sha1, 20) &&
+ !memcmp(old_sha1, new_sha1, 20))
+ return;
+
+ if (opt->reverse_diff) {
+ unsigned tmp;
+ const
+ const unsigned char *tmp_u;
+ const char *tmp_c;
+ tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+ tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
+ tmp_c = old_name; old_name = new_name; new_name = tmp_c;
+ }
+ one = alloc_filespec(old_name);
+ two = alloc_filespec(new_name);
+ fill_filespec(one, old_sha1, old_mode);
+ fill_filespec(two, new_sha1, new_mode);
+
+ /* NEEDSWORK: shouldn't this part of diffopt??? */
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+static int builtin_diff_b_f(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob,
+ const char *path)
+{
+ /* Blob vs file in the working tree*/
+ struct stat st;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (lstat(path, &st))
+ die("'%s': %s", path, strerror(errno));
+ if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+ die("'%s': not a regular file or symlink", path);
+ stuff_change(&revs->diffopt,
+ canon_mode(st.st_mode), canon_mode(st.st_mode),
+ blob[0].sha1, null_sha1,
+ blob[0].name, path);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_blobs(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob)
+{
+ /* Blobs */
+ unsigned mode = canon_mode(S_IFREG | 0644);
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ stuff_change(&revs->diffopt,
+ mode, mode,
+ blob[0].sha1, blob[1].sha1,
+ blob[1].name, blob[1].name);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_index(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int cached = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--cached"))
+ cached = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there is one revision (i.e. pending object),
+ * and there is no revision filtering parameters.
+ */
+ if (!revs->pending_objects || revs->pending_objects->next ||
+ revs->max_count != -1 || revs->min_age != -1 ||
+ revs->max_age != -1)
+ usage(builtin_diff_usage);
+ return run_diff_index(revs, cached);
+}
+
+static int builtin_diff_tree(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent)
+{
+ /* We saw two trees, ent[0] and ent[1].
+ * unless ent[0] is unintesting, they are swapped
+ */
+ const unsigned char *(sha1[2]);
+ int swap = 1;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (ent[0].item->flags & UNINTERESTING)
+ swap = 0;
+ sha1[swap] = ent[0].item->sha1;
+ sha1[1-swap] = ent[1].item->sha1;
+ diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
+ log_tree_diff_flush(revs);
+ return 0;
+}
+
+static void add_head(struct rev_info *revs)
+{
+ unsigned char sha1[20];
+ struct object *obj;
+ if (get_sha1("HEAD", sha1))
+ return;
+ obj = parse_object(sha1);
+ if (!obj)
+ return;
+ add_object(obj, &revs->pending_objects, NULL, "HEAD");
+}
+
+int cmd_diff(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+ struct object_list *list, ent[2];
+ int ents = 0, blobs = 0, paths = 0;
+ const char *path = NULL;
+ struct blobinfo blob[2];
+
+ /*
+ * We could get N tree-ish in the rev.pending_objects list.
+ * Also there could be M blobs there, and P pathspecs.
+ *
+ * N=0, M=0:
+ * cache vs files (diff-files)
+ * N=0, M=2:
+ * compare two random blobs. P must be zero.
+ * N=0, M=1, P=1:
+ * compare a blob with a working tree file.
+ *
+ * N=1, M=0:
+ * tree vs cache (diff-index --cached)
+ *
+ * N=2, M=0:
+ * tree vs tree (diff-tree)
+ *
+ * Other cases are errors.
+ */
+
+ git_config(git_diff_config);
+ init_revisions(&rev);
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
+ /* Do we have --cached and not have a pending object, then
+ * default to HEAD by hand. Eek.
+ */
+ if (!rev.pending_objects) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--"))
+ break;
+ else if (!strcmp(arg, "--cached")) {
+ add_head(&rev);
+ break;
+ }
+ }
+ }
+
+ for (list = rev.pending_objects; list; list = list->next) {
+ struct object *obj = list->item;
+ const char *name = list->name;
+ int flags = (obj->flags & UNINTERESTING);
+ if (!obj->parsed)
+ obj = parse_object(obj->sha1);
+ obj = deref_tag(obj, NULL, 0);
+ if (!obj)
+ die("invalid object '%s' given.", name);
+ if (!strcmp(obj->type, commit_type))
+ obj = &((struct commit *)obj)->tree->object;
+ if (!strcmp(obj->type, tree_type)) {
+ if (2 <= ents)
+ die("more than two trees given: '%s'", name);
+ obj->flags |= flags;
+ ent[ents].item = obj;
+ ent[ents].name = name;
+ ents++;
+ continue;
+ }
+ if (!strcmp(obj->type, blob_type)) {
+ if (2 <= blobs)
+ die("more than two blobs given: '%s'", name);
+ memcpy(blob[blobs].sha1, obj->sha1, 20);
+ blob[blobs].name = name;
+ blobs++;
+ continue;
+
+ }
+ die("unhandled object '%s' given.", name);
+ }
+ if (rev.prune_data) {
+ const char **pathspec = rev.prune_data;
+ while (*pathspec) {
+ if (!path)
+ path = *pathspec;
+ paths++;
+ pathspec++;
+ }
+ }
+
+ /*
+ * Now, do the arguments look reasonable?
+ */
+ if (!ents) {
+ switch (blobs) {
+ case 0:
+ return builtin_diff_files(&rev, argc, argv);
+ break;
+ case 1:
+ if (paths != 1)
+ usage(builtin_diff_usage);
+ return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ break;
+ case 2:
+ return builtin_diff_blobs(&rev, argc, argv, blob);
+ break;
+ default:
+ usage(builtin_diff_usage);
+ }
+ }
+ else if (blobs)
+ usage(builtin_diff_usage);
+ else if (ents == 1)
+ return builtin_diff_index(&rev, argc, argv);
+ else if (ents == 2)
+ return builtin_diff_tree(&rev, argc, argv, ent);
+ usage(builtin_diff_usage);
+}
diff --git a/builtin.h b/builtin.h
index 47408a0..52ffa52 100644
--- a/builtin.h
+++ b/builtin.h
@@ -19,5 +19,6 @@ extern int cmd_version(int argc, const c
extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
+extern int cmd_diff(int argc, const char **argv, char **envp);
#endif
diff --git a/git.c b/git.c
index 01b7e28..ff9b87a 100644
--- a/git.c
+++ b/git.c
@@ -46,6 +46,7 @@ static void handle_internal_command(int
{ "log", cmd_log },
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
+ { "diffn", cmd_diff },
};
int i;
--
1.3.1.g1d19
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH] built-in diff: assorted updates.
2006-04-29 7:19 [PATCH] built-in diff Junio C Hamano
@ 2006-04-29 9:29 ` Junio C Hamano
2006-04-29 9:45 ` [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents" Junio C Hamano
1 sibling, 0 replies; 5+ messages in thread
From: Junio C Hamano @ 2006-04-29 9:29 UTC (permalink / raw)
To: git
"git diff(n)" without --base, --ours, etc. defaults to --cc,
which usually is the same as -p unless you are in the middle of
a conflicted merge, just like the shell script version.
"git diff(n) blobA blobB path" complains and dies.
"git diff(n) tree0 tree1 tree2...treeN" does combined diff that
shows a merge of tree1..treeN to result in tree0.
Giving "-c" option to any command that defaults to "--cc" turns
off dense-combined flag.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
* Comes on top of the previous "built-in diff" patch.
Now I think this is more or less what Linus envisioned when
he started converting the rev related option parsing to use
setup_revisions(), including the exotic combinations the
shell script "git diff" does not support:
git diff(n) HEAD~10:Makefile Makefile ; blob vs any file
git diff(n) HEAD~10:Makefile HEAD~20:Makefile ; two blobs
git diff(n) v1.0.0 v1.0.0^1 v1.0.0^2 ; combine 'em
The normal ones are also supported:
git diff(n) -- Documentation ; index vs working
git diff(n) --cached ; tree vs index
git diff(n) HEAD ; tree vs working
git diff(n) HEAD~10 HEAD~8 ; two trees
git diff(n) HEAD:arch/i386 HEAD:arch/x86_64 ; two trees
builtin-diff.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++---------
combine-diff.c | 47 ++++++++++++++++++++++++++++++-----------------
diff.h | 2 ++
revision.c | 1 +
4 files changed, 79 insertions(+), 26 deletions(-)
diff --git a/builtin-diff.c b/builtin-diff.c
index 1968212..c543105 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -47,13 +47,17 @@ static int builtin_diff_files(struct rev
}
/*
* Make sure there are NO revision (i.e. pending object) parameter,
- * rev.max_count is reasonable (0 <= n <= 3),
- * there is no other revision filtering parameters.
+ * specified rev.max_count is reasonable (0 <= n <= 3), and
+ * there is no other revision filtering parameter.
*/
if (revs->pending_objects ||
revs->min_age != -1 ||
- revs->max_age != -1)
+ revs->max_age != -1 ||
+ 3 < revs->max_count)
usage(builtin_diff_usage);
+ if (revs->max_count < 0 &&
+ (revs->diffopt.output_format == DIFF_FORMAT_PATCH))
+ revs->combine_merges = revs->dense_combined_merges = 1;
/*
* Backward compatibility wart - "diff-files -s" used to
* defeat the common diff option "-s" which asked for
@@ -178,9 +182,6 @@ static int builtin_diff_tree(struct rev_
int argc, const char **argv,
struct object_list *ent)
{
- /* We saw two trees, ent[0] and ent[1].
- * unless ent[0] is unintesting, they are swapped
- */
const unsigned char *(sha1[2]);
int swap = 1;
while (1 < argc) {
@@ -191,6 +192,10 @@ static int builtin_diff_tree(struct rev_
usage(builtin_diff_usage);
argv++; argc--;
}
+
+ /* We saw two trees, ent[0] and ent[1].
+ * unless ent[0] is unintesting, they are swapped
+ */
if (ent[0].item->flags & UNINTERESTING)
swap = 0;
sha1[swap] = ent[0].item->sha1;
@@ -200,6 +205,33 @@ static int builtin_diff_tree(struct rev_
return 0;
}
+static int builtin_diff_combined(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent,
+ int ents)
+{
+ const unsigned char (*parent)[20];
+ int i;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (!revs->dense_combined_merges && !revs->combine_merges)
+ revs->dense_combined_merges = revs->combine_merges = 1;
+ parent = xmalloc(ents * sizeof(*parent));
+ /* Again, the revs are all reverse */
+ for (i = 0; i < ents; i++)
+ memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
+ diff_tree_combined(parent[0], parent + 1, ents - 1,
+ revs->dense_combined_merges, revs);
+ return 0;
+}
+
static void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
@@ -215,7 +247,7 @@ static void add_head(struct rev_info *re
int cmd_diff(int argc, const char **argv, char **envp)
{
struct rev_info rev;
- struct object_list *list, ent[2];
+ struct object_list *list, ent[100];
int ents = 0, blobs = 0, paths = 0;
const char *path = NULL;
struct blobinfo blob[2];
@@ -273,8 +305,9 @@ int cmd_diff(int argc, const char **argv
if (!strcmp(obj->type, commit_type))
obj = &((struct commit *)obj)->tree->object;
if (!strcmp(obj->type, tree_type)) {
- if (2 <= ents)
- die("more than two trees given: '%s'", name);
+ if (ARRAY_SIZE(ent) <= ents)
+ die("more than %d trees given: '%s'",
+ ARRAY_SIZE(ent), name);
obj->flags |= flags;
ent[ents].item = obj;
ent[ents].name = name;
@@ -316,6 +349,8 @@ int cmd_diff(int argc, const char **argv
return builtin_diff_b_f(&rev, argc, argv, blob, path);
break;
case 2:
+ if (paths)
+ usage(builtin_diff_usage);
return builtin_diff_blobs(&rev, argc, argv, blob);
break;
default:
@@ -328,5 +363,7 @@ int cmd_diff(int argc, const char **argv
return builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
return builtin_diff_tree(&rev, argc, argv, ent);
+ else
+ return builtin_diff_combined(&rev, argc, argv, ent, ents);
usage(builtin_diff_usage);
}
diff --git a/combine-diff.c b/combine-diff.c
index ca36f5d..8a8fe38 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -831,15 +831,16 @@ void show_combined_diff(struct combine_d
}
}
-void diff_tree_combined_merge(const unsigned char *sha1,
- int dense, struct rev_info *rev)
+void diff_tree_combined(const unsigned char *sha1,
+ const unsigned char parent[][20],
+ int num_parent,
+ int dense,
+ struct rev_info *rev)
{
struct diff_options *opt = &rev->diffopt;
- struct commit *commit = lookup_commit(sha1);
struct diff_options diffopts;
- struct commit_list *parents;
struct combine_diff_path *p, *paths = NULL;
- int num_parent, i, num_paths;
+ int i, num_paths;
int do_diffstat;
do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
@@ -849,17 +850,8 @@ void diff_tree_combined_merge(const unsi
diffopts.with_stat = 0;
diffopts.recursive = 1;
- /* count parents */
- for (parents = commit->parents, num_parent = 0;
- parents;
- parents = parents->next, num_parent++)
- ; /* nothing */
-
/* find set of paths that everybody touches */
- for (parents = commit->parents, i = 0;
- parents;
- parents = parents->next, i++) {
- struct commit *parent = parents->item;
+ for (i = 0; i < num_parent; i++) {
/* show stat against the first parent even
* when doing combined diff.
*/
@@ -867,8 +859,7 @@ void diff_tree_combined_merge(const unsi
diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
else
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
- &diffopts);
+ diff_tree_sha1(parent[i], sha1, "", &diffopts);
diffcore_std(&diffopts);
paths = intersect_paths(paths, i, num_parent);
@@ -907,3 +898,25 @@ void diff_tree_combined_merge(const unsi
free(tmp);
}
}
+
+void diff_tree_combined_merge(const unsigned char *sha1,
+ int dense, struct rev_info *rev)
+{
+ int num_parent;
+ const unsigned char (*parent)[20];
+ struct commit *commit = lookup_commit(sha1);
+ struct commit_list *parents;
+
+ /* count parents */
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ ; /* nothing */
+
+ parent = xmalloc(num_parent * sizeof(*parent));
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ memcpy(parent + num_parent, parents->item->object.sha1, 20);
+ diff_tree_combined(sha1, parent, num_parent, dense, rev);
+}
diff --git a/diff.h b/diff.h
index 7150b90..b3b2c4d 100644
--- a/diff.h
+++ b/diff.h
@@ -75,6 +75,8 @@ #define combine_diff_path_size(n, l) \
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *);
+extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
+
extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
extern void diff_addremove(struct diff_options *,
diff --git a/revision.c b/revision.c
index f2a9f25..4381d45 100644
--- a/revision.c
+++ b/revision.c
@@ -664,6 +664,7 @@ int setup_revisions(int argc, const char
}
if (!strcmp(arg, "-c")) {
revs->diff = 1;
+ revs->dense_combined_merges = 0;
revs->combine_merges = 1;
continue;
}
--
1.3.1.ga0c5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents"
2006-04-29 7:19 [PATCH] built-in diff Junio C Hamano
2006-04-29 9:29 ` [PATCH] built-in diff: assorted updates Junio C Hamano
@ 2006-04-29 9:45 ` Junio C Hamano
2006-04-29 15:38 ` Johannes Schindelin
1 sibling, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2006-04-29 9:45 UTC (permalink / raw)
To: git
A short-hand "rev^#" is understood to be "all parents of the
named commit" with this patch. So you can do
git show v1.0.0^#
to view the parents of a merge commit,
gitk ^v1.0.0^# v1.0.4
to view the log between two revs (including the bottom one), and
git diff --cc v1.1.0 v1.0.0^#
to inspect what got changed from the merge parents of v1.0.0 to v1.1.0.
This might be just my shiny new toy that is not very useful in
practice. I needed it to do the multi-tree diff on Len's
infamous 12-way Octopus; typing "diff --cc funmerge funmerge^1
funmerge^2 funmerge^3 ..." was too painful.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
revision.c | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/revision.c b/revision.c
index f2a9f25..194f35b 100644
--- a/revision.c
+++ b/revision.c
@@ -477,6 +477,36 @@ static void handle_all(struct rev_info *
for_each_ref(handle_one_ref);
}
+static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
+{
+ unsigned char sha1[20];
+ struct object *it;
+ struct commit *commit;
+ struct commit_list *parents;
+
+ if (*arg == '^') {
+ flags ^= UNINTERESTING;
+ arg++;
+ }
+ if (get_sha1(arg, sha1))
+ return 0;
+ while (1) {
+ it = get_reference(revs, arg, sha1, 0);
+ if (strcmp(it->type, tag_type))
+ break;
+ memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
+ }
+ if (strcmp(it->type, commit_type))
+ return 0;
+ commit = (struct commit *)it;
+ for (parents = commit->parents; parents; parents = parents->next) {
+ it = &parents->item->object;
+ it->flags |= flags;
+ add_pending_object(revs, it, arg);
+ }
+ return 1;
+}
+
void init_revisions(struct rev_info *revs)
{
memset(revs, 0, sizeof(*revs));
@@ -746,6 +776,13 @@ int setup_revisions(int argc, const char
}
*dotdot = '.';
}
+ dotdot = strstr(arg, "^#");
+ if (dotdot && !dotdot[2]) {
+ *dotdot = 0;
+ if (add_parents_only(revs, arg, flags))
+ continue;
+ *dotdot = '^';
+ }
local_flags = 0;
if (*arg == '^') {
local_flags = UNINTERESTING;
--
1.3.1.ga0c5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents"
2006-04-29 9:45 ` [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents" Junio C Hamano
@ 2006-04-29 15:38 ` Johannes Schindelin
2006-04-29 16:59 ` Linus Torvalds
0 siblings, 1 reply; 5+ messages in thread
From: Johannes Schindelin @ 2006-04-29 15:38 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Hi,
On Sat, 29 Apr 2006, Junio C Hamano wrote:
> A short-hand "rev^#" is understood to be "all parents of the
> named commit" with this patch.
Just my 2/100: Why not "rev^*"? I could remember that more easily.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents"
2006-04-29 15:38 ` Johannes Schindelin
@ 2006-04-29 16:59 ` Linus Torvalds
0 siblings, 0 replies; 5+ messages in thread
From: Linus Torvalds @ 2006-04-29 16:59 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, git
On Sat, 29 Apr 2006, Johannes Schindelin wrote:
>
> > A short-hand "rev^#" is understood to be "all parents of the
> > named commit" with this patch.
>
> Just my 2/100: Why not "rev^*"? I could remember that more easily.
Yeah, that (or ^@ - to match shell "$1" "$2" .. "$@") was my reaction too.
The "rev^#" thing should to my mind return the _number_ of parents, the
same way "$#" does in shell. I actually pronounce that '#' character
mentally as "number", but maybe that's just because I'm totally mentally
damaged by shell programming, and everybody else probably calls it "hash"
(and some people apparently call it "pound", for some really sick reason).
So "rev^#" literally reads as "revision parent number" to me. Useful?
Maybe. Maybe not.
Linus
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2006-04-29 17:00 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-29 7:19 [PATCH] built-in diff Junio C Hamano
2006-04-29 9:29 ` [PATCH] built-in diff: assorted updates Junio C Hamano
2006-04-29 9:45 ` [PATCH/RFC] Extended SHA1 -- "rev^#" syntax to mean "all parents" Junio C Hamano
2006-04-29 15:38 ` Johannes Schindelin
2006-04-29 16:59 ` Linus Torvalds
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).