instead of further extending the porcelain git-diff(1), a diff plumbing
command should be used.
As there is not an existing diff plumbing command that handles blob
diffs, introduce git-diff-blob(1) which generates a single diff between
a specified pair of blobs following how the same operation is done in
git-diff(1). While git-diff-blob(1) functionality is a direct subset of
git-diff(1), a subsequent patch extends it to provide a new plumbing
related feature.
The surrounding setup required for the new builtin is also added. An
existing test for blob diffs through git-diff(1) is also modified to
reuse its test cases for git-diff-blob(1).
Signed-off-by: Justin Tobler <jltobler@gmail.com>
---
.gitignore | 1 +
Documentation/git-diff-blob.txt | 29 ++++++++
Documentation/meson.build | 1 +
Makefile | 1 +
builtin.h | 1 +
builtin/diff-blob.c | 117 ++++++++++++++++++++++++++++++++
command-list.txt | 1 +
git.c | 1 +
meson.build | 1 +
t/t4063-diff-blobs.sh | 100 ++++++++++++++-------------
10 files changed, 205 insertions(+), 48 deletions(-)
create mode 100644 Documentation/git-diff-blob.txt
create mode 100644 builtin/diff-blob.c
@@ -52,6 +52,7 @@
/git-daemon
/git-diagnose
/git-diff
+/git-diff-blob
/git-diff-files
/git-diff-index
/git-diff-tree
new file mode 100644
@@ -0,0 +1,29 @@
+git-diff-blob(1)
+================
+
+NAME
+----
+git-diff-blob - Compares the content and mode of two specified blobs
+
+
+SYNOPSIS
+--------
+[verse]
+'git diff-blob' <blob> <blob>
+
+DESCRIPTION
+-----------
+Compare the content and mode of two specified blobs.
+
+OPTIONS
+-------
+<blob>::
+ The id of a blob object or path-scoped revision that resolves to a blob.
+
+include::pretty-formats.txt[]
+
+include::diff-format.txt[]
+
+GIT
+---
+Part of the linkgit:git[1] suite
@@ -39,6 +39,7 @@ manpages = {
'git-daemon.txt' : 1,
'git-describe.txt' : 1,
'git-diagnose.txt' : 1,
+ 'git-diff-blob.txt' : 1,
'git-diff-files.txt' : 1,
'git-diff-index.txt' : 1,
'git-difftool.txt' : 1,
@@ -1235,6 +1235,7 @@ BUILTIN_OBJS += builtin/credential-store.o
BUILTIN_OBJS += builtin/credential.o
BUILTIN_OBJS += builtin/describe.o
BUILTIN_OBJS += builtin/diagnose.o
+BUILTIN_OBJS += builtin/diff-blob.o
BUILTIN_OBJS += builtin/diff-files.o
BUILTIN_OBJS += builtin/diff-index.o
BUILTIN_OBJS += builtin/diff-tree.o
@@ -152,6 +152,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix, struct reposit
int cmd_diff_files(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff_index(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_diff_blob(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff_tree(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_difftool(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_env__helper(int argc, const char **argv, const char *prefix, struct repository *repo);
new file mode 100644
@@ -0,0 +1,117 @@
+#include "builtin.h"
+#include "config.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "object.h"
+#include "parse-options.h"
+#include "revision.h"
+
+static void diff_blobs(struct object_array_entry *old_blob,
+ struct object_array_entry *new_blob,
+ struct diff_options *opts)
+{
+ const unsigned mode = canon_mode(S_IFREG | 0644);
+ struct object_id old_oid = old_blob->item->oid;
+ struct object_id new_oid = new_blob->item->oid;
+ unsigned old_mode = old_blob->mode;
+ unsigned new_mode = new_blob->mode;
+ char *old_path = old_blob->path;
+ char *new_path = new_blob->path;
+ struct diff_filespec *old, *new;
+
+ if (old_mode == S_IFINVALID)
+ old_mode = mode;
+
+ if (new_mode == S_IFINVALID)
+ new_mode = mode;
+
+ if (!old_path)
+ old_path = old_blob->name;
+
+ if (!new_path)
+ new_path = new_blob->name;
+
+ if (!is_null_oid(&old_oid) && !is_null_oid(&new_oid) &&
+ oideq(&old_oid, &new_oid) && (old_mode == new_mode))
+ return;
+
+ if (opts->flags.reverse_diff) {
+ SWAP(old_oid, new_oid);
+ SWAP(old_mode, new_mode);
+ SWAP(old_path, new_path);
+ }
+
+ if (opts->prefix &&
+ (strncmp(old_path, opts->prefix, opts->prefix_length) ||
+ strncmp(new_path, opts->prefix, opts->prefix_length)))
+ return;
+
+ old = alloc_filespec(old_path);
+ new = alloc_filespec(new_path);
+
+ fill_filespec(old, &old_oid, 1, old_mode);
+ fill_filespec(new, &new_oid, 1, new_mode);
+
+ diff_queue(&diff_queued_diff, old, new);
+ diffcore_std(opts);
+ diff_flush(opts);
+}
+
+int cmd_diff_blob(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ struct object_array_entry *old_blob, *new_blob;
+ struct rev_info revs;
+ int ret;
+
+ const char * const usage[] = {
+ N_("git diff-blob <blob> <blob>"),
+ NULL
+ };
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage,
+ PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_ARGV0);
+
+ repo_config(repo, git_diff_basic_config, NULL);
+ prepare_repo_settings(repo);
+ repo->settings.command_requires_full_index = 0;
+
+ repo_init_revisions(repo, &revs, prefix);
+ revs.abbrev = 0;
+ revs.diff = 1;
+ revs.disable_stdin = 1;
+
+ prefix = precompose_argv_prefix(argc, argv, prefix);
+ argc = setup_revisions(argc, argv, &revs, NULL);
+
+ if (!revs.diffopt.output_format)
+ revs.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ switch (revs.pending.nr) {
+ case 2:
+ old_blob = &revs.pending.objects[0];
+ new_blob = &revs.pending.objects[1];
+
+ if (old_blob->item->type != OBJ_BLOB)
+ die("object %s is not a blob", old_blob->name);
+
+ if (new_blob->item->type != OBJ_BLOB)
+ die("object %s is not a blob", new_blob->name);
+
+ diff_blobs(old_blob, new_blob, &revs.diffopt);
+
+ break;
+ default:
+ usage_with_options(usage, options);
+ }
+
+ ret = diff_result_code(&revs);
+ release_revisions(&revs);
+
+ return ret;
+}
@@ -93,6 +93,7 @@ git-daemon synchingrepositories
git-describe mainporcelain
git-diagnose ancillaryinterrogators
git-diff mainporcelain info
+git-diff-blob plumbinginterrogators
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff-tree plumbinginterrogators
@@ -540,6 +540,7 @@ static struct cmd_struct commands[] = {
{ "describe", cmd_describe, RUN_SETUP },
{ "diagnose", cmd_diagnose, RUN_SETUP_GENTLY },
{ "diff", cmd_diff, NO_PARSEOPT },
+ { "diff-blob", cmd_diff_blob, RUN_SETUP | NO_PARSEOPT },
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
@@ -503,6 +503,7 @@ builtin_sources = [
'builtin/credential.c',
'builtin/describe.c',
'builtin/diagnose.c',
+ 'builtin/diff-blob.c',
'builtin/diff-files.c',
'builtin/diff-index.c',
'builtin/diff-tree.c',
@@ -1,12 +1,14 @@
#!/bin/sh
-test_description='test direct comparison of blobs via git-diff'
+test_description='test direct comparison of blobs via git-diff and git-diff-blob'
. ./test-lib.sh
+commands="diff diff-blob"
+
run_diff () {
# use full-index to make it easy to match the index line
- git diff --full-index "$@" >diff
+ git $1 --full-index $2 $3 >diff
}
check_index () {
@@ -37,61 +39,63 @@ test_expect_success 'create some blobs' '
sha1_two=$(git rev-parse HEAD:two)
'
-test_expect_success 'diff by sha1' '
- run_diff $sha1_one $sha1_two
-'
-test_expect_success 'index of sha1 diff' '
- check_index $sha1_one $sha1_two
-'
-test_expect_success 'sha1 diff uses arguments as paths' '
- check_paths $sha1_one $sha1_two
-'
-test_expect_success 'sha1 diff has no mode change' '
- ! grep mode diff
-'
-
-test_expect_success 'diff by tree:path (run)' '
- run_diff HEAD:one HEAD:two
-'
-test_expect_success 'index of tree:path diff' '
- check_index $sha1_one $sha1_two
-'
-test_expect_success 'tree:path diff uses filenames as paths' '
- check_paths one two
-'
-test_expect_success 'tree:path diff shows mode change' '
- check_mode 100644 100755
-'
-
-test_expect_success 'diff by ranged tree:path' '
- run_diff HEAD:one..HEAD:two
-'
-test_expect_success 'index of ranged tree:path diff' '
- check_index $sha1_one $sha1_two
-'
-test_expect_success 'ranged tree:path diff uses filenames as paths' '
- check_paths one two
-'
-test_expect_success 'ranged tree:path diff shows mode change' '
- check_mode 100644 100755
-'
-
-test_expect_success 'diff blob against file' '
- run_diff HEAD:one two
+test_expect_success 'diff blob against file (git-diff)' '
+ run_diff diff HEAD:one two
'
-test_expect_success 'index of blob-file diff' '
+test_expect_success 'index of blob-file diff (git-diff)' '
check_index $sha1_one $sha1_two
'
-test_expect_success 'blob-file diff uses filename as paths' '
+test_expect_success 'blob-file diff uses filename as paths (git-diff)' '
check_paths one two
'
-test_expect_success FILEMODE 'blob-file diff shows mode change' '
+test_expect_success FILEMODE 'blob-file diff shows mode change (git-diff)' '
check_mode 100644 100755
'
-test_expect_success 'blob-file diff prefers filename to sha1' '
- run_diff $sha1_one two &&
+test_expect_success 'blob-file diff prefers filename to sha1 (git-diff)' '
+ run_diff diff $sha1_one two &&
check_paths two two
'
+for cmd in $commands; do
+ test_expect_success "diff by sha1 (git-$cmd)" '
+ run_diff $cmd $sha1_one $sha1_two
+ '
+ test_expect_success "index of sha1 diff (git-$cmd)" '
+ check_index $sha1_one $sha1_two
+ '
+ test_expect_success "sha1 diff uses arguments as paths (git-$cmd)" '
+ check_paths $sha1_one $sha1_two
+ '
+ test_expect_success "sha1 diff has no mode change (git-$cmd)" '
+ ! grep mode diff
+ '
+
+ test_expect_success "diff by tree:path (run) (git-$cmd)" '
+ run_diff $cmd HEAD:one HEAD:two
+ '
+ test_expect_success "index of tree:path diff (git-$cmd)" '
+ check_index $sha1_one $sha1_two
+ '
+ test_expect_success "tree:path diff uses filenames as paths (git-$cmd)" '
+ check_paths one two
+ '
+ test_expect_success "tree:path diff shows mode change (git-$cmd)" '
+ check_mode 100644 100755
+ '
+
+ test_expect_success "diff by ranged tree:path (git-$cmd)" '
+ run_diff $cmd HEAD:one..HEAD:two
+ '
+ test_expect_success "index of ranged tree:path diff (git-$cmd)" '
+ check_index $sha1_one $sha1_two
+ '
+ test_expect_success "ranged tree:path diff uses filenames as paths (git-$cmd)" '
+ check_paths one two
+ '
+ test_expect_success "ranged tree:path diff shows mode change (git-$cmd)" '
+ check_mode 100644 100755
+ '
+done
+
test_done