Commit b5212858 by Miha

Merge remote-tracking branch 'remotes/upstream/development' into development

parents 3536c168 494be429
...@@ -101,23 +101,4 @@ at the ...@@ -101,23 +101,4 @@ at the
## Starter Projects ## Starter Projects
So, you want to start helping out with `libgit2`? That's fantastic? We See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).
welcome contributions and we promise we'll try to be nice.
If you want to jump in, you can look at our issues list to see if there
are any unresolved issues to jump in on. Also, here is a list of some
smaller project ideas that could help you become familiar with the code
base and make a nice first step:
* Look at the `examples/` programs, find an existing one that mirrors a
core Git command and add a missing command-line option. There are many
gaps right now and this helps demonstrate how to use the library.
* Pick a Git command that is not emulates in `examples/` and write a new
example that mirrors the behavior. Examples don't have to be perfect
emulations, but should demonstrate how to use the libgit2 APIs to get
results that are similar to Git commands. This lets you (and us) easily
exercise a particular facet of the API and measure compatability and
feature parity with core git.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
confusing much more easily.
Projects For LibGit2
====================
So, you want to start helping out with `libgit2`? That's fantastic! We
welcome contributions and we promise we'll try to be nice.
This is a list of libgit2 related projects that new contributors can take
on. It includes a number of good starter projects and well as some larger
ideas that no one is actively working on.
## Before You Start
Please start by reading the README.md, CONTRIBUTING.md, and CONVENTIONS.md
files before diving into one of these projects. Those will explain our
work flow and coding conventions to help ensure that your work will be
easily integrated into libgit2.
Next, work through the build instructions and make sure you can clone the
repository, compile it, and run the tests successfully. That will make
sure that your development environment is set up correctly and you are
ready to start on libgit2 development.
## Starter Projects
These are good small projects to get started with libgit2.
* Look at the `examples/` programs, find an existing one that mirrors a
core Git command and add a missing command-line option. There are many
gaps right now and this helps demonstrate how to use the library. Here
are some specific ideas:
* Add the `--minimal` flag to `examples/diff.c` since the `libgit2`
diff API now has a flag to support it
* Add the `--patience` flag to `examples/diff.c` since it is also now
supported.
* Add the `--shortstat` flag to `examples/diff.c` based on the work
that was done to add `--numstat` already.
* Fix the `examples/diff.c` implementation of the `-B`
(a.k.a. `--break-rewrites`) command line option to actually look for
the optional `[<n>][/<m>]` configuration values. There is an
existing comment that reads `/* TODO: parse thresholds */`. The
trick to this one will be doing it in a manner that is clean and
simple, but still handles the various cases correctly (e.g. `-B/70%`
is apparently a legal setting).
* Implement the `--log-size` option for `examples/log.c`. I think all
the data is available, you would just need to add the code into the
`print_commit()` routine (along with a way of passing the option
into that function).
* For `examples/log.c`, implement any one of `--author=<...>`,
`--committer=<...>`, or `--grep=<...>` but just use simple string
match with `strstr()` instead of full regular expression
matching. (I.e. I'm suggesting implementing this as if
`--fixed-strings` was always turned on, because it will be a simpler
project.)
* As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that
libgit2 supports it in the revwalk API.
* Pick a Git command that is not already emulated in `examples/` and write
a new example that mirrors the behavior. Examples don't have to be
perfect emulations, but should demonstrate how to use the libgit2 APIs
to get results that are similar to Git commands. This lets you (and us)
easily exercise a particular facet of the API and measure compatability
and feature parity with core git.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
confusing much more easily.
If none of these appeal to you, take a look at our issues list to see if
there are any unresolved issues you'd like to jump in on.
## Larger Projects
These are ideas for larger projects mostly taken from our backlog of
[Issues](https://github.com/libgit2/libgit2/issues). Please don't dive
into one of these as a first project for libgit2 - we'd rather get to
know you first by successfully shipping your work on one of the smaller
projects above.
* Port part of the Git test suite to run against the command line emulation
in examples/
* Fix symlink support for files in the .git directory (i.e. don't overwrite
the symlinks when writing the file contents back out)
* Implement a 'git describe' like API
* Add hooks API to enumerate and manage hooks (not run them at this point)
* Isolate logic of ignore evaluation into a standalone API
* Upgrade internal libxdiff code to latest from core Git
* Add a hashtable lookup for files in the index instead of binary search
every time
...@@ -4,9 +4,9 @@ libgit2 - the Git linkable library ...@@ -4,9 +4,9 @@ libgit2 - the Git linkable library
[![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2) [![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639) [![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639)
`libgit2` is a portable, pure C implementation of the Git core methods provided as a `libgit2` is a portable, pure C implementation of the Git core methods
re-entrant linkable library with a solid API, allowing you to write native provided as a re-entrant linkable library with a solid API, allowing you to
speed custom Git applications in any language with bindings. write native speed custom Git applications in any language with bindings.
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special `libgit2` is licensed under a **very permissive license** (GPLv2 with a special
Linking Exception). This basically means that you can link it (unmodified) Linking Exception). This basically means that you can link it (unmodified)
...@@ -30,8 +30,9 @@ Additionally, the example code has been released to the public domain (see the ...@@ -30,8 +30,9 @@ Additionally, the example code has been released to the public domain (see the
What It Can Do What It Can Do
============== ==============
`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM `libgit2` is already very usable and is being used in production for many
and also powering Microsoft's Visual Studio tools for Git. The library provides: applications including the GitHub.com site, in Plastic SCM and also powering
Microsoft's Visual Studio tools for Git. The library provides:
* SHA conversions, formatting and shortening * SHA conversions, formatting and shortening
* abstracted ODB backend system * abstracted ODB backend system
...@@ -199,14 +200,16 @@ we can add it to the list. ...@@ -199,14 +200,16 @@ we can add it to the list.
How Can I Contribute? How Can I Contribute?
================================== ==================================
Check the [contribution guidelines](CONTRIBUTING.md). Check the [contribution guidelines](CONTRIBUTING.md) to understand our
workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of
[good starting projects](PROJECTS.md).
License License
================================== ==================================
`libgit2` is under GPL2 **with linking exemption**. This means you
can link to and use the library from any program, proprietary or open source; paid `libgit2` is under GPL2 **with linking exemption**. This means you can link to
or gratis. However, you cannot modify libgit2 and distribute it without and use the library from any program, proprietary or open source; paid or
gratis. However, you cannot modify libgit2 and distribute it without
supplying the source. supplying the source.
See the COPYING file for the full license text. See the [COPYING file](COPYING) for the full license text.
...@@ -31,6 +31,7 @@ struct opts { ...@@ -31,6 +31,7 @@ struct opts {
int M; int M;
int start_line; int start_line;
int end_line; int end_line;
int F;
}; };
static void parse_opts(struct opts *o, int argc, char *argv[]); static void parse_opts(struct opts *o, int argc, char *argv[]);
...@@ -52,6 +53,7 @@ int main(int argc, char *argv[]) ...@@ -52,6 +53,7 @@ int main(int argc, char *argv[])
parse_opts(&o, argc, argv); parse_opts(&o, argc, argv);
if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES; if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES; if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
/** Open the repository. */ /** Open the repository. */
check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL); check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL);
...@@ -147,6 +149,7 @@ static void usage(const char *msg, const char *arg) ...@@ -147,6 +149,7 @@ static void usage(const char *msg, const char *arg)
fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n"); fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n");
fprintf(stderr, " -M find line moves within and across files\n"); fprintf(stderr, " -M find line moves within and across files\n");
fprintf(stderr, " -C find line copies within and across files\n"); fprintf(stderr, " -C find line copies within and across files\n");
fprintf(stderr, " -F follow only the first parent commits\n");
fprintf(stderr, "\n"); fprintf(stderr, "\n");
exit(1); exit(1);
} }
...@@ -175,6 +178,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) ...@@ -175,6 +178,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
o->M = 1; o->M = 1;
else if (!strcasecmp(a, "-C")) else if (!strcasecmp(a, "-C"))
o->C = 1; o->C = 1;
else if (!strcasecmp(a, "-F"))
o->F = 1;
else if (!strcasecmp(a, "-L")) { else if (!strcasecmp(a, "-L")) {
i++; a = argv[i]; i++; a = argv[i];
if (i >= argc) fatal("Not enough arguments to -L", NULL); if (i >= argc) fatal("Not enough arguments to -L", NULL);
......
...@@ -269,19 +269,23 @@ static void diff_print_numstat(git_diff *diff) ...@@ -269,19 +269,23 @@ static void diff_print_numstat(git_diff *diff)
{ {
git_patch *patch; git_patch *patch;
const git_diff_delta *delta; const git_diff_delta *delta;
size_t i; size_t d, ndeltas = git_diff_num_deltas(diff);
size_t ndeltas;
size_t nadditions, ndeletions; size_t nadditions, ndeletions;
ndeltas = git_diff_num_deltas(diff);
for (i = 0; i < ndeltas; i++){ for (d = 0; d < ndeltas; d++){
check_lg2( check_lg2(
git_patch_from_diff(&patch, diff, i), git_patch_from_diff(&patch, diff, d),
"generating patch from diff", NULL); "generating patch from diff", NULL);
check_lg2( check_lg2(
git_patch_line_stats(NULL, &nadditions, &ndeletions, patch), git_patch_line_stats(NULL, &nadditions, &ndeletions, patch),
"generating the number of additions and deletions", NULL); "generating the number of additions and deletions", NULL);
delta = git_patch_get_delta(patch); delta = git_patch_get_delta(patch);
printf("%u\t%u\t%s\n", nadditions, ndeletions, delta->new_file.path);
} printf("%ld\t%ld\t%s\n",
(long)nadditions, (long)ndeletions, delta->new_file.path);
git_patch_free(patch); git_patch_free(patch);
}
} }
...@@ -28,9 +28,6 @@ ...@@ -28,9 +28,6 @@
# his/her consent on a patch-by-patch basis. # his/her consent on a patch-by-patch basis.
# "???" means the person is a prominent contributor who has # "???" means the person is a prominent contributor who has
# not yet made his/her standpoint clear. # not yet made his/her standpoint clear.
# "ign" means the authors consent is ignored for the purpose
# of libification. This is because the author has contributed
# to areas that aren't interesting for the library.
# #
# Please try to keep the list alphabetically ordered. It will # Please try to keep the list alphabetically ordered. It will
# help in case we get all 600-ish git.git authors on it. # help in case we get all 600-ish git.git authors on it.
...@@ -61,7 +58,6 @@ ok Linus Torvalds <torvalds@linux-foundation.org> ...@@ -61,7 +58,6 @@ ok Linus Torvalds <torvalds@linux-foundation.org>
ok Lukas Sandström <lukass@etek.chalmers.se> ok Lukas Sandström <lukass@etek.chalmers.se>
ok Matthieu Moy <Matthieu.Moy@imag.fr> ok Matthieu Moy <Matthieu.Moy@imag.fr>
ok Michael Haggerty <mhagger@alum.mit.edu> ok Michael Haggerty <mhagger@alum.mit.edu>
ign Mike McCormack <mike@codeweavers.com> (imap-send)
ok Nicolas Pitre <nico@fluxnic.net> <nico@cam.org> ok Nicolas Pitre <nico@fluxnic.net> <nico@cam.org>
ok Paolo Bonzini <bonzini@gnu.org> ok Paolo Bonzini <bonzini@gnu.org>
ok Paul Kocher <paul@cryptography.com> ok Paul Kocher <paul@cryptography.com>
...@@ -70,7 +66,6 @@ ok Petr Onderka <gsvick@gmail.com> ...@@ -70,7 +66,6 @@ ok Petr Onderka <gsvick@gmail.com>
ok Pierre Habouzit <madcoder@debian.org> ok Pierre Habouzit <madcoder@debian.org>
ok Pieter de Bie <pdebie@ai.rug.nl> ok Pieter de Bie <pdebie@ai.rug.nl>
ok René Scharfe <rene.scharfe@lsrfire.ath.cx> ok René Scharfe <rene.scharfe@lsrfire.ath.cx>
ign Robert Shearman <rob@codeweavers.com> (imap-send)
ok Sebastian Schuberth <sschuberth@gmail.com> ok Sebastian Schuberth <sschuberth@gmail.com>
ok Shawn O. Pearce <spearce@spearce.org> ok Shawn O. Pearce <spearce@spearce.org>
ok Steffen Prohaska <prohaska@zib.de> ok Steffen Prohaska <prohaska@zib.de>
......
...@@ -40,6 +40,9 @@ typedef enum { ...@@ -40,6 +40,9 @@ typedef enum {
* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES. * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
* NOT IMPLEMENTED. */ * NOT IMPLEMENTED. */
GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3), GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
/** Restrict the search of commits to those reachable following only the
* first parents. */
GIT_BLAME_FIRST_PARENT = (1<<4),
} git_blame_flag_t; } git_blame_flag_t;
/** /**
......
...@@ -1013,6 +1013,39 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( ...@@ -1013,6 +1013,39 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
git_diff_line_cb line_cb, git_diff_line_cb line_cb,
void *payload); void *payload);
/**
* Directly run a diff between two buffers.
*
* Even more than with `git_diff_blobs`, comparing two buffer lacks
* context, so the `git_diff_file` parameters to the callbacks will be
* faked a la the rules for `git_diff_blobs()`.
*
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options
* @param file_cb Callback for "file"; made once if there is a diff; can be NULL
* @param hunk_cb Callback for each hunk in diff; can be NULL
* @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_buffers(
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const void *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *options,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *payload);
GIT_END_DECL GIT_END_DECL
......
...@@ -106,6 +106,34 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer( ...@@ -106,6 +106,34 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer(
const git_diff_options *opts); const git_diff_options *opts);
/** /**
* Directly generate a patch from the difference between two buffers.
*
* This is just like `git_diff_buffers()` except it generates a patch
* object for the difference instead of directly making callbacks. You can
* use the standard `git_patch` accessor functions to read the patch
* data, and you must call `git_patch_free()` on the patch when done.
*
* @param out The generated patch; NULL on error
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @param opts Options for diff, or NULL for default options
* @return 0 on success or error code < 0
*/
GIT_EXTERN(int) git_patch_from_buffers(
git_patch **out,
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const char *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *opts);
/**
* Free a git_patch object. * Free a git_patch object.
*/ */
GIT_EXTERN(void) git_patch_free(git_patch *patch); GIT_EXTERN(void) git_patch_free(git_patch *patch);
......
...@@ -485,12 +485,14 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt ...@@ -485,12 +485,14 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
git_blame__origin *sg_buf[16]; git_blame__origin *sg_buf[16];
git_blame__origin *porigin, **sg_origin = sg_buf; git_blame__origin *porigin, **sg_origin = sg_buf;
GIT_UNUSED(opt);
num_parents = git_commit_parentcount(commit); num_parents = git_commit_parentcount(commit);
if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
/* Stop at oldest specified commit */ /* Stop at oldest specified commit */
num_parents = 0; num_parents = 0;
else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1)
/* Limit search to the first parent */
num_parents = 1;
if (!num_parents) { if (!num_parents) {
git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
goto finish; goto finish;
......
...@@ -58,6 +58,7 @@ int git_branch_create( ...@@ -58,6 +58,7 @@ int git_branch_create(
const git_signature *signature, const git_signature *signature,
const char *log_message) const char *log_message)
{ {
int is_head = 0;
git_reference *branch = NULL; git_reference *branch = NULL;
git_buf canonical_branch_name = GIT_BUF_INIT, git_buf canonical_branch_name = GIT_BUF_INIT,
log_message_buf = GIT_BUF_INIT; log_message_buf = GIT_BUF_INIT;
...@@ -65,6 +66,18 @@ int git_branch_create( ...@@ -65,6 +66,18 @@ int git_branch_create(
assert(branch_name && commit && ref_out); assert(branch_name && commit && ref_out);
assert(git_object_owner((const git_object *)commit) == repository); assert(git_object_owner((const git_object *)commit) == repository);
if (git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
if ((is_head = git_branch_is_head(branch)) < 0) {
error = is_head;
goto cleanup;
}
}
if (is_head && force) {
giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
"the current HEAD of the repository.", git_reference_name(branch));
goto cleanup;
}
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
goto cleanup; goto cleanup;
......
...@@ -127,57 +127,38 @@ int git_diff_file_content__init_from_diff( ...@@ -127,57 +127,38 @@ int git_diff_file_content__init_from_diff(
return diff_file_content_init_common(fc, &diff->opts); return diff_file_content_init_common(fc, &diff->opts);
} }
int git_diff_file_content__init_from_blob( int git_diff_file_content__init_from_src(
git_diff_file_content *fc, git_diff_file_content *fc,
git_repository *repo, git_repository *repo,
const git_diff_options *opts, const git_diff_options *opts,
const git_blob *blob, const git_diff_file_content_src *src,
git_diff_file *as_file) git_diff_file *as_file)
{ {
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
fc->repo = repo; fc->repo = repo;
fc->file = as_file; fc->file = as_file;
fc->blob = blob; fc->blob = src->blob;
if (!blob) { if (!src->blob && !src->buf) {
fc->flags |= GIT_DIFF_FLAG__NO_DATA; fc->flags |= GIT_DIFF_FLAG__NO_DATA;
} else { } else {
fc->flags |= GIT_DIFF_FLAG__LOADED; fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
fc->file->size = git_blob_rawsize(blob);
fc->file->mode = GIT_FILEMODE_BLOB; fc->file->mode = GIT_FILEMODE_BLOB;
git_oid_cpy(&fc->file->id, git_blob_id(blob));
fc->map.len = (size_t)fc->file->size; if (src->blob) {
fc->map.data = (char *)git_blob_rawcontent(blob); fc->file->size = git_blob_rawsize(src->blob);
} git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
return diff_file_content_init_common(fc, opts);
}
int git_diff_file_content__init_from_raw(
git_diff_file_content *fc,
git_repository *repo,
const git_diff_options *opts,
const char *buf,
size_t buflen,
git_diff_file *as_file)
{
memset(fc, 0, sizeof(*fc));
fc->repo = repo;
fc->file = as_file;
if (!buf) { fc->map.len = (size_t)fc->file->size;
fc->flags |= GIT_DIFF_FLAG__NO_DATA; fc->map.data = (char *)git_blob_rawcontent(src->blob);
} else { } else {
fc->flags |= GIT_DIFF_FLAG__LOADED; fc->file->size = src->buflen;
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
fc->file->size = buflen;
fc->file->mode = GIT_FILEMODE_BLOB;
git_odb_hash(&fc->file->id, buf, buflen, GIT_OBJ_BLOB);
fc->map.len = buflen; fc->map.len = src->buflen;
fc->map.data = (char *)buf; fc->map.data = (char *)src->buf;
}
} }
return diff_file_content_init_common(fc, opts); return diff_file_content_init_common(fc, opts);
......
...@@ -31,19 +31,21 @@ extern int git_diff_file_content__init_from_diff( ...@@ -31,19 +31,21 @@ extern int git_diff_file_content__init_from_diff(
size_t delta_index, size_t delta_index,
bool use_old); bool use_old);
extern int git_diff_file_content__init_from_blob( typedef struct {
git_diff_file_content *fc, const git_blob *blob;
git_repository *repo, const void *buf;
const git_diff_options *opts, size_t buflen;
const git_blob *blob, const char *as_path;
git_diff_file *as_file); } git_diff_file_content_src;
#define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) }
#define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) }
extern int git_diff_file_content__init_from_raw( extern int git_diff_file_content__init_from_src(
git_diff_file_content *fc, git_diff_file_content *fc,
git_repository *repo, git_repository *repo,
const git_diff_options *opts, const git_diff_options *opts,
const char *buf, const git_diff_file_content_src *src,
size_t buflen,
git_diff_file *as_file); git_diff_file *as_file);
/* this loads the blob/file-on-disk as needed */ /* this loads the blob/file-on-disk as needed */
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "common.h" #include "common.h"
#include "git2/blob.h"
#include "diff.h" #include "diff.h"
#include "diff_file.h" #include "diff_file.h"
#include "diff_driver.h" #include "diff_driver.h"
...@@ -334,38 +335,45 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) ...@@ -334,38 +335,45 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
return error; return error;
} }
static int diff_patch_from_blobs( static int diff_patch_from_sources(
diff_patch_with_delta *pd, diff_patch_with_delta *pd,
git_xdiff_output *xo, git_xdiff_output *xo,
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
git_repository *repo = git_repository *repo =
new_blob ? git_object_owner((const git_object *)new_blob) : oldsrc->blob ? git_blob_owner(oldsrc->blob) :
old_blob ? git_object_owner((const git_object *)old_blob) : NULL; newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
const git_blob *tmp_blob; void *tmp = lfile; lfile = rfile; rfile = tmp;
const char *tmp_path; tmp = ldata; ldata = rdata; rdata = tmp;
tmp_blob = old_blob; old_blob = new_blob; new_blob = tmp_blob;
tmp_path = old_path; old_path = new_path; new_path = tmp_path;
} }
pd->patch.delta = &pd->delta; pd->patch.delta = &pd->delta;
pd->delta.old_file.path = old_path; if (!oldsrc->as_path) {
pd->delta.new_file.path = new_path; if (newsrc->as_path)
oldsrc->as_path = newsrc->as_path;
else
oldsrc->as_path = newsrc->as_path = "file";
}
else if (!newsrc->as_path)
newsrc->as_path = oldsrc->as_path;
lfile->path = oldsrc->as_path;
rfile->path = newsrc->as_path;
if ((error = git_diff_file_content__init_from_blob( if ((error = git_diff_file_content__init_from_src(
&pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)) < 0 || ldata, repo, opts, oldsrc, lfile)) < 0 ||
(error = git_diff_file_content__init_from_blob( (error = git_diff_file_content__init_from_src(
&pd->patch.nfile, repo, opts, new_blob, &pd->delta.new_file)) < 0) rdata, repo, opts, newsrc, rfile)) < 0)
return error; return error;
return diff_single_generate(pd, xo); return diff_single_generate(pd, xo);
...@@ -400,11 +408,9 @@ static int diff_patch_with_delta_alloc( ...@@ -400,11 +408,9 @@ static int diff_patch_with_delta_alloc(
return 0; return 0;
} }
int git_diff_blobs( static int diff_from_sources(
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts, const git_diff_options *opts,
git_diff_file_cb file_cb, git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb, git_diff_hunk_cb hunk_cb,
...@@ -420,26 +426,19 @@ int git_diff_blobs( ...@@ -420,26 +426,19 @@ int git_diff_blobs(
&xo.output, opts, file_cb, hunk_cb, data_cb, payload); &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
if (!old_path && new_path)
old_path = new_path;
else if (!new_path && old_path)
new_path = old_path;
memset(&pd, 0, sizeof(pd)); memset(&pd, 0, sizeof(pd));
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts); error = diff_patch_from_sources(&pd, &xo, oldsrc, newsrc, opts);
git_patch_free(&pd.patch); git_patch_free(&pd.patch);
return error; return error;
} }
int git_patch_from_blobs( static int patch_from_sources(
git_patch **out, git_patch **out,
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
...@@ -449,17 +448,15 @@ int git_patch_from_blobs( ...@@ -449,17 +448,15 @@ int git_patch_from_blobs(
assert(out); assert(out);
*out = NULL; *out = NULL;
if (diff_patch_with_delta_alloc(&pd, &old_path, &new_path) < 0) if ((error = diff_patch_with_delta_alloc(
return -1; &pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
return error;
memset(&xo, 0, sizeof(xo)); memset(&xo, 0, sizeof(xo));
diff_output_to_patch(&xo.output, &pd->patch); diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
error = diff_patch_from_blobs( if (!(error = diff_patch_from_sources(pd, &xo, oldsrc, newsrc, opts)))
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
if (!error)
*out = (git_patch *)pd; *out = (git_patch *)pd;
else else
git_patch_free((git_patch *)pd); git_patch_free((git_patch *)pd);
...@@ -467,46 +464,38 @@ int git_patch_from_blobs( ...@@ -467,46 +464,38 @@ int git_patch_from_blobs(
return error; return error;
} }
static int diff_patch_from_blob_and_buffer( int git_diff_blobs(
diff_patch_with_delta *pd,
git_xdiff_output *xo,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path, const char *old_path,
const char *buf, const git_blob *new_blob,
size_t buflen, const char *new_path,
const char *buf_path, const git_diff_options *opts,
const git_diff_options *opts) git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb data_cb,
void *payload)
{ {
int error = 0; git_diff_file_content_src osrc =
git_repository *repo = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
old_blob ? git_object_owner((const git_object *)old_blob) : NULL; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); return diff_from_sources(
&osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
pd->patch.delta = &pd->delta; }
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
pd->delta.old_file.path = buf_path;
pd->delta.new_file.path = old_path;
if (!(error = git_diff_file_content__init_from_raw(
&pd->patch.ofile, repo, opts, buf, buflen, &pd->delta.old_file)))
error = git_diff_file_content__init_from_blob(
&pd->patch.nfile, repo, opts, old_blob, &pd->delta.new_file);
} else {
pd->delta.old_file.path = old_path;
pd->delta.new_file.path = buf_path;
if (!(error = git_diff_file_content__init_from_blob(
&pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)))
error = git_diff_file_content__init_from_raw(
&pd->patch.nfile, repo, opts, buf, buflen, &pd->delta.new_file);
}
if (error < 0)
return error;
return diff_single_generate(pd, xo); int git_patch_from_blobs(
git_patch **out,
const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
return patch_from_sources(out, &osrc, &nsrc, opts);
} }
int git_diff_blob_to_buffer( int git_diff_blob_to_buffer(
...@@ -521,27 +510,12 @@ int git_diff_blob_to_buffer( ...@@ -521,27 +510,12 @@ int git_diff_blob_to_buffer(
git_diff_line_cb data_cb, git_diff_line_cb data_cb,
void *payload) void *payload)
{ {
int error = 0; git_diff_file_content_src osrc =
diff_patch_with_delta pd; GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_xdiff_output xo; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
memset(&xo, 0, sizeof(xo)); return diff_from_sources(
diff_output_init( &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && buf_path)
old_path = buf_path;
else if (!buf_path && old_path)
buf_path = old_path;
memset(&pd, 0, sizeof(pd));
error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
git_patch_free(&pd.patch);
return error;
} }
int git_patch_from_blob_and_buffer( int git_patch_from_blob_and_buffer(
...@@ -553,29 +527,49 @@ int git_patch_from_blob_and_buffer( ...@@ -553,29 +527,49 @@ int git_patch_from_blob_and_buffer(
const char *buf_path, const char *buf_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; git_diff_file_content_src osrc =
diff_patch_with_delta *pd; GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_xdiff_output xo; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
assert(out); return patch_from_sources(out, &osrc, &nsrc, opts);
*out = NULL; }
if (diff_patch_with_delta_alloc(&pd, &old_path, &buf_path) < 0)
return -1;
memset(&xo, 0, sizeof(xo));
diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blob_and_buffer(
pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
if (!error) int git_diff_buffers(
*out = (git_patch *)pd; const void *old_buf,
else size_t old_len,
git_patch_free((git_patch *)pd); const char *old_path,
const void *new_buf,
size_t new_len,
const char *new_path,
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb data_cb,
void *payload)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
return diff_from_sources(
&osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
}
return error; int git_patch_from_buffers(
git_patch **out,
const void *old_buf,
size_t old_len,
const char *old_path,
const char *new_buf,
size_t new_len,
const char *new_path,
const git_diff_options *opts)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
return patch_from_sources(out, &osrc, &nsrc, opts);
} }
int git_patch_from_diff( int git_patch_from_diff(
......
...@@ -303,3 +303,18 @@ void test_blame_simple__can_restrict_to_newish_commits(void) ...@@ -303,3 +303,18 @@ void test_blame_simple__can_restrict_to_newish_commits(void)
check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt"); check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt"); check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt");
} }
void test_blame_simple__can_restrict_to_first_parent_commits(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
opts.flags |= GIT_BLAME_FIRST_PARENT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "bc7c5ac2", "b.txt");
}
...@@ -67,6 +67,25 @@ void test_refs_branches_create__can_force_create_over_an_existing_branch(void) ...@@ -67,6 +67,25 @@ void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
cl_assert_equal_s("refs/heads/br2", git_reference_name(branch)); cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
} }
void test_refs_branches_create__cannot_force_create_over_current_branch(void)
{
const git_oid *oid;
git_reference *branch2;
retrieve_known_commit(&target, repo);
cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
cl_assert_equal_s("refs/heads/master", git_reference_name(branch2));
cl_assert_equal_i(true, git_branch_is_head(branch2));
oid = git_reference_target(branch2);
cl_git_fail_with(-1, git_branch_create(&branch, repo, "master", target, 1, NULL, NULL));
branch = NULL;
cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
cl_git_pass(git_oid_cmp(git_reference_target(branch), oid));
git_reference_free(branch2);
}
void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
{ {
retrieve_known_commit(&target, repo); retrieve_known_commit(&target, repo);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment