* CVSImport - spaces in CVS path
From: Yojoa @ 2016-11-30 20:56 UTC (permalink / raw)
To: git
I'm in the process of moving an entire collection of cvs modules into git.
I'm working in Mac Yosemite. Everything is working fine except for one
thing. A couple of the CVS modules have spaces in the paths. Below is what
my command line looks like. When the path has spaces I've tried putting it
in single and double quotes and using escape characters. None of that
matters. I always get a message that it can't read dir/dir/path and then
messages that it can't find the modules "with" or "spaces".
git cvsimport -v -d :pserver:MYLOGIN:/usr/local/cvsroot/
dir/dir/PathWithNoSpaces/dir
git cvsimport -v -d :pserver:MYLOGIN:/usr/local/cvsroot/ dir/dir/path with
spaces/dir
--
View this message in context: http://git.661346.n2.nabble.com/CVSImport-spaces-in-CVS-path-tp7657459.html
Sent from the git mailing list archive at Nabble.com.
^ permalink raw reply
* Re: [PATCHv2 4/4] submodule: add embed-git-dir function
From: Junio C Hamano @ 2016-11-30 21:39 UTC (permalink / raw)
To: Stefan Beller
Cc: Duy Nguyen, Brandon Williams, Git Mailing List, Jonathan Nieder,
Jens Lehmann, Heiko Voigt
In-Reply-To: <CAGZ79kar0F7x5U2yZ30ZnWZ9b=EJA=1nT8rxTMRVJPggyFS_XA@mail.gmail.com>
Stefan Beller <sbeller@google.com> writes:
> git relocate-git-dir (--into-workingtree|--into-gitdir) \
I am not sure if you meant this as a submodule-specific subcommand
or more general helper. "into-workingtree" suggests to me that it
is submodule specific, so I'll base my response on that assumption.
Would there ever be a situation where you already have submodule
repositories in the right place (according to the more modern
practice, to keep them in .git/modules/ of superproject) and want to
move them to embed them in worktrees of submodules? I do not think
of any.
If there is no such situation, I do not think we want a verb that is
direction-neutral (e.g. "move" or "relocate") with two options.
Rather we would want "git submodule unembed-git-dir" or something
like that.
^ permalink raw reply
* Re: "git add -p ." raises an unexpected "warning: empty strings as pathspecs will be made invalid in upcoming releases. please use . instead if you meant to match all paths"
From: Kevin Daudt @ 2016-11-30 21:11 UTC (permalink / raw)
To: Peter Urda; +Cc: git
In-Reply-To: <CAEnOLdvG=SoKFxeJ_pLmamGj_8osC+28TSg+pbFLLTr+ZLcpQA@mail.gmail.com>
On Wed, Nov 30, 2016 at 12:31:49PM -0800, Peter Urda wrote:
> After upgrading to version 2.11.0 I am getting a warning about empty
> strings as pathspecs while using 'patch'
>
> - Ran 'git add -p .' from the root of my git repository.
>
> - I was able to normally stage my changes, but was presented with a
> "warning: empty strings as pathspecs will be made invalid in upcoming
> releases. please use . instead if you meant to match all paths"
> message.
>
> - I expected no warning message since I included a "." with my original command.
>
> I believe that I should not be seeing this warning message as I
> included the requested "." pathspec.
>
> ~ Peter Urda
>
> http://urda.cc
I can reproduce this. Note that it only happens when you specify '-p'.
Without the --patch option, the warning does not appear.
^ permalink raw reply
* Re: [PATCH v2] tag, branch, for-each-ref: add --ignore-case for sorting and filtering
From: Junio C Hamano @ 2016-11-30 21:21 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, karthik.188
In-Reply-To: <20161130123502.12973-1-pclouds@gmail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> This option makes sorting ignore case, which is great when you have
> branches named bug-12-do-something, Bug-12-do-some-more and
> BUG-12-do-what and want to group them together. Sorting externally may
> not be an option because we lose coloring and column layout from
> git-branch and git-tag.
>
> The same could be said for filtering, but it's probably less important
> because you can always go with the ugly pattern [bB][uU][gG]-* if you're
> desperate.
But of course --ignore-case is of course much easier.
> You can't have case-sensitive filtering and case-insensitive sorting (or
> the other way around) with this though. But who would want that?
I do not feel uncomfortable declaring that the answer to that
question is "nobody" ;-)
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> v2 has a different approach, and I think it's a better one even with
> that unanswered question above.
Yeah, I think this would be easier to use.
> @@ -512,15 +512,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
> if (filter->verbose)
> maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
>
> - /*
> - * If no sorting parameter is given then we default to sorting
> - * by 'refname'. This would give us an alphabetically sorted
> - * array with the 'HEAD' ref at the beginning followed by
> - * local branches 'refs/heads/...' and finally remote-tacking
> - * branches 'refs/remotes/...'.
> - */
> - if (!sorting)
> - sorting = ref_default_sorting();
So it is now a BUG() to give sorting==NULL to this function, which
is OK and I do not think we even need an assert() for it (i.e. the
code with the patch looks good).
> @@ -744,6 +739,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
> if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
> filter.kind |= FILTER_REFS_DETACHED_HEAD;
> filter.name_patterns = argv;
> + /*
> + * If no sorting parameter is given then we default to sorting
> + * by 'refname'. This would give us an alphabetically sorted
> + * array with the 'HEAD' ref at the beginning followed by
> + * local branches 'refs/heads/...' and finally remote-tacking
> + * branches 'refs/remotes/...'.
> + */
> + if (!sorting)
> + sorting = ref_default_sorting();
> + sorting->ignore_case = icase;
> print_ref_list(&filter, sorting);
> print_columns(&output, colopts, NULL);
> string_list_clear(&output, 0);
... and the fallback is moved to the caller, which makes sense.
> diff --git a/ref-filter.c b/ref-filter.c
> index f5f7a70..bd98010 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -1231,8 +1231,14 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)
> * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
> * matches "refs/heads/mas*", too).
> */
> -static int match_pattern(const char **patterns, const char *refname)
> +static int match_pattern(const struct ref_filter *filter, const char *refname)
> {
> + const char **patterns = filter->name_patterns;
> + unsigned flags = 0;
> +
> + if (filter->ignore_case)
> + flags |= WM_CASEFOLD;
> +
Ahh, OK. My reading stuttered when seeing "sorting and filtering"
in the option description for "git tag", but this makes perfect
sense. Good job.
> @@ -1255,9 +1261,15 @@ static int match_pattern(const char **patterns, const char *refname)
> * matches a pattern "refs/heads/" but not "refs/heads/m") or a
> * wildcard (e.g. the same ref matches "refs/heads/m*", too).
> */
> -static int match_name_as_path(const char **pattern, const char *refname)
> +static int match_name_as_path(const struct ref_filter *filter, const char *refname)
> {
> + const char **pattern = filter->name_patterns;
> int namelen = strlen(refname);
> + unsigned flags = WM_PATHNAME;
> +
> + if (filter->ignore_case)
> + flags |= WM_CASEFOLD;
> +
Likewise. Very simple and nicely done.
> @@ -1536,18 +1548,20 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
> struct atom_value *va, *vb;
> int cmp;
> cmp_type cmp_type = used_atom[s->atom].type;
> + int (*cmp_fn)(const char *, const char *);
>
> get_ref_atom_value(a, s->atom, &va);
> get_ref_atom_value(b, s->atom, &vb);
> + cmp_fn = s->ignore_case ? strcasecmp : strcmp;
> if (s->version)
> cmp = versioncmp(va->s, vb->s);
> else if (cmp_type == FIELD_STR)
> - cmp = strcmp(va->s, vb->s);
> + cmp = cmp_fn(va->s, vb->s);
> else {
> if (va->ul < vb->ul)
> cmp = -1;
> else if (va->ul == vb->ul)
> - cmp = strcmp(a->refname, b->refname);
> + cmp = cmp_fn(a->refname, b->refname);
> else
> cmp = 1;
> }
OK.
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> index c6a3ccb..fad79e8 100755
> --- a/t/t3203-branch-output.sh
> +++ b/t/t3203-branch-output.sh
> @@ -89,6 +89,11 @@ test_expect_success 'git branch --list -v pattern shows branch summaries' '
> awk "{print \$NF}" <tmp >actual &&
> test_cmp expect actual
> '
> +test_expect_success 'git branch --ignore-case --list -v pattern shows branch summaries' '
> + git branch --list --ignore-case -v BRANCH* >tmp &&
> + awk "{print \$NF}" <tmp >actual &&
> + test_cmp expect actual
> +'
The way the test ensures it found only branch-one and branch-two
looks very sloppy, but that was inherited from the existing one
before this new one, so I'll let it pass.
> @@ -196,4 +201,21 @@ test_expect_success 'local-branch symrefs shortened properly' '
> test_cmp expect actual
> '
>
> +test_expect_success 'sort branches, ignore case' '
> + (
> + git init sort-icase &&
> + cd sort-icase &&
> + test_commit initial &&
> + git branch branch-one &&
> + git branch BRANCH-two &&
> + git branch --list -i | awk "{print \$NF}" >actual &&
> + cat >expected <<-\EOF &&
> + branch-one
> + BRANCH-two
> + master
> + EOF
> + test_cmp expected actual
> + )
> +'
Is there an existing test that uses refs with mixed cases, i.e. the
result of listing sorts differently with and without the -i option?
If not, this one should test output from both cases to ensure that
the command run without -i stays case sensitive.
> test_done
> diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
> index 8b0f71a..2d9cae3 100755
> --- a/t/t7004-tag.sh
> +++ b/t/t7004-tag.sh
> @@ -27,6 +27,23 @@ test_expect_success 'listing all tags in an empty tree should output nothing' '
> test $(git tag | wc -l) -eq 0
> '
>
> +test_expect_success 'sort tags, ignore case' '
> + (
> + git init sort &&
> + cd sort &&
> + test_commit initial &&
> + git tag tag-one &&
> + git tag TAG-two &&
> + git tag -l -i >actual &&
> + cat >expected <<-\EOF &&
> + initial
> + tag-one
> + TAG-two
> + EOF
> + test_cmp expected actual
> + )
> +'
Ditto.
> test_expect_success 'looking for a tag in an empty tree should fail' \
> '! (tag_exists mytag)'
>
> @@ -81,6 +98,9 @@ test_expect_success 'listing all tags if one exists should output that tag' '
> test_expect_success 'listing a tag using a matching pattern should succeed' \
> 'git tag -l mytag'
>
> +test_expect_success 'listing a tag using a matching pattern should succeed' \
> + 'git tag -l --ignore-case MYTAG'
The existing one before this one merely says that "git tag -l" must
exit with 0 status code, no?
IOW, even "git tag -l no-such-tag-anywhere && echo OK" yields OK.
So there is not much point replicating it with "-i", unless you want
to say that "git tag -i -l" also must exit with 0 status code.
> test_expect_success \
> 'listing a tag using a matching pattern should output that tag' \
> 'test $(git tag -l mytag) = mytag'
I think the new one would want to mimic this one instead. Look for
MYTAG with the -i option and see it output mytag (in lowercase).
^ permalink raw reply
* Re: [PATCH] difftool.c: mark a file-local symbol with static
From: Jeff King @ 2016-11-30 21:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ramsay Jones, Johannes Schindelin, GIT Mailing-list
In-Reply-To: <xmqqtwaod7ly.fsf@gitster.mtv.corp.google.com>
On Wed, Nov 30, 2016 at 12:40:25PM -0800, Junio C Hamano wrote:
> Ramsay Jones <ramsay@ramsayjones.plus.com> writes:
>
> > [I have fixed my config.mak file now, so I don't see the warning
> > anymore! Having -Wno-format-zero-length in DEVELOPER_CFLAGS, or
> > not, is a separate matter.]
>
> I suspect that 658df95a4a ("add DEVELOPER makefile knob to check for
> acknowledged warnings", 2016-02-25) took it from me (namely, Make
> script in my 'todo' branch). In turn, I added it to my set of flags
> in order to squelch this exact warning, so...
For anybody interested in the history, we started using this when
status_printf() got the format attribute. Relevant patch and discussion:
http://public-inbox.org/git/20130710002328.GC19423@sigill.intra.peff.net/T/#u
We went with disabling the warning because it really is wrong. It makes
an assumption that calling a format function with an empty string
doesn't do anything, but that's only true of the stock printf functions.
Our custom functions _do_ have a side effect in this case.
The other options are to have a special function for "print a warning
(or status) line with no content". Or to teach those functions to handle
newlines specially. We've often discussed that you should be able to do:
warning("foo\nbar");
and have it print:
warning: foo
warning: bar
That's useful in itself, and would probably make cases like this easier
to handle, too. But it's a pretty big change. Another option would be to
just teach formatting functions to handle a single "\n" as a synonym for
the empty string (or even detect trailing newlines and avoid appending
our own in that case). That would mean you could do:
warning("\n");
to print a blank line. That's arguably more obvious about the intent to
a reader (I say arguably because the new behavior _is_ subtle if you
happen to know that warning() usually appends a newline).
Anyway. Those are all options, but I don't think there is any problem
with sticking with warning("") for now. It is not the first part of the
code that tickles the format-zero-length warning.
-Peff
^ permalink raw reply
* [RFC/PATCH v3 01/16] Add initial external odb support
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
From: Jeff King <peff@peff.net>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
Makefile | 2 +
cache.h | 9 ++
external-odb.c | 115 +++++++++++++++++++++++
external-odb.h | 8 ++
odb-helper.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++
odb-helper.h | 25 +++++
sha1_file.c | 64 ++++++++++---
t/t0400-external-odb.sh | 48 ++++++++++
8 files changed, 495 insertions(+), 15 deletions(-)
create mode 100644 external-odb.c
create mode 100644 external-odb.h
create mode 100644 odb-helper.c
create mode 100644 odb-helper.h
create mode 100755 t/t0400-external-odb.sh
diff --git a/Makefile b/Makefile
index f53fcc90d7..88e78da886 100644
--- a/Makefile
+++ b/Makefile
@@ -747,6 +747,7 @@ LIB_OBJS += ewah/ewah_bitmap.o
LIB_OBJS += ewah/ewah_io.o
LIB_OBJS += ewah/ewah_rlw.o
LIB_OBJS += exec_cmd.o
+LIB_OBJS += external-odb.o
LIB_OBJS += fetch-pack.o
LIB_OBJS += fsck.o
LIB_OBJS += gettext.o
@@ -779,6 +780,7 @@ LIB_OBJS += notes-cache.o
LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
LIB_OBJS += object.o
+LIB_OBJS += odb-helper.o
LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-bitmap-write.o
LIB_OBJS += pack-check.o
diff --git a/cache.h b/cache.h
index a50a61a197..b419b9b7ce 100644
--- a/cache.h
+++ b/cache.h
@@ -885,6 +885,12 @@ const char *git_path_shallow(void);
extern const char *sha1_file_name(const unsigned char *sha1);
/*
+ * Like sha1_file_name, but return the filename within a specific alternate
+ * object directory. Shares the same static buffer with sha1_file_name.
+ */
+extern const char *sha1_file_name_alt(const char *objdir, const unsigned char *sha1);
+
+/*
* Return the name of the (local) packfile with the specified sha1 in
* its name. The return value is a pointer to memory that is
* overwritten each time this function is called.
@@ -1135,6 +1141,8 @@ extern int do_check_packed_object_crc;
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
+extern int create_object_tmpfile(struct strbuf *tmp, const char *filename);
+extern void close_sha1_file(int fd);
extern int finalize_object_file(const char *tmpfile, const char *filename);
extern int has_sha1_pack(const unsigned char *sha1);
@@ -1409,6 +1417,7 @@ extern void read_info_alternates(const char * relative_base, int depth);
extern char *compute_alternate_path(const char *path, struct strbuf *err);
typedef int alt_odb_fn(struct alternate_object_database *, void *);
extern int foreach_alt_odb(alt_odb_fn, void*);
+extern void prepare_external_alt_odb(void);
/*
* Allocate a "struct alternate_object_database" but do _not_ actually
diff --git a/external-odb.c b/external-odb.c
new file mode 100644
index 0000000000..1ccfa99a01
--- /dev/null
+++ b/external-odb.c
@@ -0,0 +1,115 @@
+#include "cache.h"
+#include "external-odb.h"
+#include "odb-helper.h"
+
+static struct odb_helper *helpers;
+static struct odb_helper **helpers_tail = &helpers;
+
+static struct odb_helper *find_or_create_helper(const char *name, int len)
+{
+ struct odb_helper *o;
+
+ for (o = helpers; o; o = o->next)
+ if (!strncmp(o->name, name, len) && !o->name[len])
+ return o;
+
+ o = odb_helper_new(name, len);
+ *helpers_tail = o;
+ helpers_tail = &o->next;
+
+ return o;
+}
+
+static int external_odb_config(const char *var, const char *value, void *data)
+{
+ struct odb_helper *o;
+ const char *key, *dot;
+
+ if (!skip_prefix(var, "odb.", &key))
+ return 0;
+ dot = strrchr(key, '.');
+ if (!dot)
+ return 0;
+
+ o = find_or_create_helper(key, dot - key);
+ key = dot + 1;
+
+ if (!strcmp(key, "command"))
+ return git_config_string(&o->cmd, var, value);
+
+ return 0;
+}
+
+static void external_odb_init(void)
+{
+ static int initialized;
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ git_config(external_odb_config, NULL);
+}
+
+const char *external_odb_root(void)
+{
+ static const char *root;
+ if (!root)
+ root = git_pathdup("objects/external");
+ return root;
+}
+
+int external_odb_has_object(const unsigned char *sha1)
+{
+ struct odb_helper *o;
+
+ external_odb_init();
+
+ for (o = helpers; o; o = o->next)
+ if (odb_helper_has_object(o, sha1))
+ return 1;
+ return 0;
+}
+
+int external_odb_fetch_object(const unsigned char *sha1)
+{
+ struct odb_helper *o;
+ const char *path;
+
+ if (!external_odb_has_object(sha1))
+ return -1;
+
+ path = sha1_file_name_alt(external_odb_root(), sha1);
+ safe_create_leading_directories_const(path);
+ prepare_external_alt_odb();
+
+ for (o = helpers; o; o = o->next) {
+ struct strbuf tmpfile = STRBUF_INIT;
+ int ret;
+ int fd;
+
+ if (!odb_helper_has_object(o, sha1))
+ continue;
+
+ fd = create_object_tmpfile(&tmpfile, path);
+ if (fd < 0) {
+ strbuf_release(&tmpfile);
+ return -1;
+ }
+
+ if (odb_helper_fetch_object(o, sha1, fd) < 0) {
+ close(fd);
+ unlink(tmpfile.buf);
+ strbuf_release(&tmpfile);
+ continue;
+ }
+
+ close_sha1_file(fd);
+ ret = finalize_object_file(tmpfile.buf, path);
+ strbuf_release(&tmpfile);
+ if (!ret)
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/external-odb.h b/external-odb.h
new file mode 100644
index 0000000000..2397477684
--- /dev/null
+++ b/external-odb.h
@@ -0,0 +1,8 @@
+#ifndef EXTERNAL_ODB_H
+#define EXTERNAL_ODB_H
+
+const char *external_odb_root(void);
+int external_odb_has_object(const unsigned char *sha1);
+int external_odb_fetch_object(const unsigned char *sha1);
+
+#endif /* EXTERNAL_ODB_H */
diff --git a/odb-helper.c b/odb-helper.c
new file mode 100644
index 0000000000..244bc86792
--- /dev/null
+++ b/odb-helper.c
@@ -0,0 +1,239 @@
+#include "cache.h"
+#include "object.h"
+#include "argv-array.h"
+#include "odb-helper.h"
+#include "run-command.h"
+#include "sha1-lookup.h"
+
+struct odb_helper *odb_helper_new(const char *name, int namelen)
+{
+ struct odb_helper *o;
+
+ o = xcalloc(1, sizeof(*o));
+ o->name = xmemdupz(name, namelen);
+
+ return o;
+}
+
+struct odb_helper_cmd {
+ struct argv_array argv;
+ struct child_process child;
+};
+
+static void prepare_helper_command(struct argv_array *argv, const char *cmd,
+ const char *fmt, va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addstr(&buf, cmd);
+ strbuf_addch(&buf, ' ');
+ strbuf_vaddf(&buf, fmt, ap);
+
+ argv_array_push(argv, buf.buf);
+ strbuf_release(&buf);
+}
+
+__attribute__((format (printf,3,4)))
+static int odb_helper_start(struct odb_helper *o,
+ struct odb_helper_cmd *cmd,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ memset(cmd, 0, sizeof(*cmd));
+ argv_array_init(&cmd->argv);
+
+ if (!o->cmd)
+ return -1;
+
+ va_start(ap, fmt);
+ prepare_helper_command(&cmd->argv, o->cmd, fmt, ap);
+ va_end(ap);
+
+ cmd->child.argv = cmd->argv.argv;
+ cmd->child.use_shell = 1;
+ cmd->child.no_stdin = 1;
+ cmd->child.out = -1;
+
+ if (start_command(&cmd->child) < 0) {
+ argv_array_clear(&cmd->argv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int odb_helper_finish(struct odb_helper *o,
+ struct odb_helper_cmd *cmd)
+{
+ int ret = finish_command(&cmd->child);
+ argv_array_clear(&cmd->argv);
+ if (ret) {
+ warning("odb helper '%s' reported failure", o->name);
+ return -1;
+ }
+ return 0;
+}
+
+static int parse_object_line(struct odb_helper_object *o, const char *line)
+{
+ char *end;
+ if (get_sha1_hex(line, o->sha1) < 0)
+ return -1;
+
+ line += 40;
+ if (*line++ != ' ')
+ return -1;
+
+ o->size = strtoul(line, &end, 10);
+ if (line == end || *end++ != ' ')
+ return -1;
+
+ o->type = type_from_string(end);
+ return 0;
+}
+
+static int odb_helper_object_cmp(const void *va, const void *vb)
+{
+ const struct odb_helper_object *a = va, *b = vb;
+ return hashcmp(a->sha1, b->sha1);
+}
+
+static void odb_helper_load_have(struct odb_helper *o)
+{
+ struct odb_helper_cmd cmd;
+ FILE *fh;
+ struct strbuf line = STRBUF_INIT;
+
+ if (o->have_valid)
+ return;
+ o->have_valid = 1;
+
+ if (odb_helper_start(o, &cmd, "have") < 0)
+ return;
+
+ fh = xfdopen(cmd.child.out, "r");
+ while (strbuf_getline(&line, fh) != EOF) {
+ ALLOC_GROW(o->have, o->have_nr+1, o->have_alloc);
+ if (parse_object_line(&o->have[o->have_nr], line.buf) < 0) {
+ warning("bad 'have' input from odb helper '%s': %s",
+ o->name, line.buf);
+ break;
+ }
+ o->have_nr++;
+ }
+
+ strbuf_release(&line);
+ fclose(fh);
+ odb_helper_finish(o, &cmd);
+
+ qsort(o->have, o->have_nr, sizeof(*o->have), odb_helper_object_cmp);
+}
+
+static struct odb_helper_object *odb_helper_lookup(struct odb_helper *o,
+ const unsigned char *sha1)
+{
+ int idx;
+
+ odb_helper_load_have(o);
+ idx = sha1_entry_pos(o->have, sizeof(*o->have), 0,
+ 0, o->have_nr, o->have_nr,
+ sha1);
+ if (idx < 0)
+ return NULL;
+ return &o->have[idx];
+}
+
+int odb_helper_has_object(struct odb_helper *o, const unsigned char *sha1)
+{
+ return !!odb_helper_lookup(o, sha1);
+}
+
+int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
+ int fd)
+{
+ struct odb_helper_object *obj;
+ struct odb_helper_cmd cmd;
+ unsigned long total_got;
+ git_zstream stream;
+ int zret = Z_STREAM_END;
+ git_SHA_CTX hash;
+ unsigned char real_sha1[20];
+
+ obj = odb_helper_lookup(o, sha1);
+ if (!obj)
+ return -1;
+
+ if (odb_helper_start(o, &cmd, "get %s", sha1_to_hex(sha1)) < 0)
+ return -1;
+
+ memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
+ git_SHA1_Init(&hash);
+ total_got = 0;
+
+ for (;;) {
+ unsigned char buf[4096];
+ int r;
+
+ r = xread(cmd.child.out, buf, sizeof(buf));
+ if (r < 0) {
+ error("unable to read from odb helper '%s': %s",
+ o->name, strerror(errno));
+ close(cmd.child.out);
+ odb_helper_finish(o, &cmd);
+ git_inflate_end(&stream);
+ return -1;
+ }
+ if (r == 0)
+ break;
+
+ write_or_die(fd, buf, r);
+
+ stream.next_in = buf;
+ stream.avail_in = r;
+ do {
+ unsigned char inflated[4096];
+ unsigned long got;
+
+ stream.next_out = inflated;
+ stream.avail_out = sizeof(inflated);
+ zret = git_inflate(&stream, Z_SYNC_FLUSH);
+ got = sizeof(inflated) - stream.avail_out;
+
+ git_SHA1_Update(&hash, inflated, got);
+ /* skip header when counting size */
+ if (!total_got) {
+ const unsigned char *p = memchr(inflated, '\0', got);
+ if (p)
+ got -= p - inflated + 1;
+ else
+ got = 0;
+ }
+ total_got += got;
+ } while (stream.avail_in && zret == Z_OK);
+ }
+
+ close(cmd.child.out);
+ git_inflate_end(&stream);
+ git_SHA1_Final(real_sha1, &hash);
+ if (odb_helper_finish(o, &cmd))
+ return -1;
+ if (zret != Z_STREAM_END) {
+ warning("bad zlib data from odb helper '%s' for %s",
+ o->name, sha1_to_hex(sha1));
+ return -1;
+ }
+ if (total_got != obj->size) {
+ warning("size mismatch from odb helper '%s' for %s (%lu != %lu)",
+ o->name, sha1_to_hex(sha1), total_got, obj->size);
+ return -1;
+ }
+ if (hashcmp(real_sha1, sha1)) {
+ warning("sha1 mismatch from odb helper '%s' for %s (got %s)",
+ o->name, sha1_to_hex(sha1), sha1_to_hex(real_sha1));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/odb-helper.h b/odb-helper.h
new file mode 100644
index 0000000000..0f704f9452
--- /dev/null
+++ b/odb-helper.h
@@ -0,0 +1,25 @@
+#ifndef ODB_HELPER_H
+#define ODB_HELPER_H
+
+struct odb_helper {
+ const char *name;
+ const char *cmd;
+
+ struct odb_helper_object {
+ unsigned char sha1[20];
+ unsigned long size;
+ enum object_type type;
+ } *have;
+ int have_nr;
+ int have_alloc;
+ int have_valid;
+
+ struct odb_helper *next;
+};
+
+struct odb_helper *odb_helper_new(const char *name, int namelen);
+int odb_helper_has_object(struct odb_helper *o, const unsigned char *sha1);
+int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
+ int fd);
+
+#endif /* ODB_HELPER_H */
diff --git a/sha1_file.c b/sha1_file.c
index 9c86d1924a..6d68157e30 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -26,6 +26,7 @@
#include "mru.h"
#include "list.h"
#include "mergesort.h"
+#include "external-odb.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -185,12 +186,12 @@ static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
}
}
-const char *sha1_file_name(const unsigned char *sha1)
+const char *sha1_file_name_alt(const char *objdir, const unsigned char *sha1)
{
static struct strbuf buf = STRBUF_INIT;
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s/", get_object_directory());
+ strbuf_addf(&buf, "%s/", objdir);
fill_sha1_path(&buf, sha1);
return buf.buf;
@@ -210,6 +211,11 @@ static const char *alt_sha1_path(struct alternate_object_database *alt,
return buf->buf;
}
+const char *sha1_file_name(const unsigned char *sha1)
+{
+ return sha1_file_name_alt(get_object_directory(), sha1);
+}
+
/*
* Return the name of the pack or index file with the specified sha1
* in its filename. *base and *name are scratch space that must be
@@ -545,6 +551,21 @@ int foreach_alt_odb(alt_odb_fn fn, void *cb)
return r;
}
+void prepare_external_alt_odb(void)
+{
+ static int linked_external;
+ const char *path;
+
+ if (linked_external)
+ return;
+
+ path = external_odb_root();
+ if (!access(path, F_OK)) {
+ link_alt_odb_entry(path, NULL, 0, "");
+ linked_external = 1;
+ }
+}
+
void prepare_alt_odb(void)
{
const char *alt;
@@ -559,6 +580,7 @@ void prepare_alt_odb(void)
link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
read_info_alternates(get_object_directory(), 0);
+ prepare_external_alt_odb();
}
/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
@@ -599,7 +621,7 @@ static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
if (check_and_freshen_file(path, freshen))
return 1;
}
- return 0;
+ return external_odb_has_object(sha1);
}
static int check_and_freshen(const unsigned char *sha1, int freshen)
@@ -1631,21 +1653,15 @@ static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
return -1;
}
-static int open_sha1_file(const unsigned char *sha1)
+static int open_sha1_file_alt(const unsigned char *sha1)
{
- int fd;
struct alternate_object_database *alt;
- int most_interesting_errno;
-
- fd = git_open(sha1_file_name(sha1));
- if (fd >= 0)
- return fd;
- most_interesting_errno = errno;
+ int most_interesting_errno = errno;
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
const char *path = alt_sha1_path(alt, sha1);
- fd = git_open(path);
+ int fd = git_open(path);
if (fd >= 0)
return fd;
if (most_interesting_errno == ENOENT)
@@ -1655,6 +1671,24 @@ static int open_sha1_file(const unsigned char *sha1)
return -1;
}
+static int open_sha1_file(const unsigned char *sha1)
+{
+ int fd;
+
+ fd = git_open(sha1_file_name(sha1));
+ if (fd >= 0)
+ return fd;
+
+ fd = open_sha1_file_alt(sha1);
+ if (fd >= 0)
+ return fd;
+
+ if (!external_odb_fetch_object(sha1))
+ fd = open_sha1_file_alt(sha1);
+
+ return fd;
+}
+
void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
{
void *map;
@@ -3139,7 +3173,7 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
}
/* Finalize a file on disk, and close it. */
-static void close_sha1_file(int fd)
+void close_sha1_file(int fd)
{
if (fsync_object_files)
fsync_or_die(fd, "sha1 file");
@@ -3163,7 +3197,7 @@ static inline int directory_size(const char *filename)
* We want to avoid cross-directory filename renames, because those
* can have problems on various filesystems (FAT, NFS, Coda).
*/
-static int create_tmpfile(struct strbuf *tmp, const char *filename)
+int create_object_tmpfile(struct strbuf *tmp, const char *filename)
{
int fd, dirlen = directory_size(filename);
@@ -3203,7 +3237,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
static struct strbuf tmp_file = STRBUF_INIT;
const char *filename = sha1_file_name(sha1);
- fd = create_tmpfile(&tmp_file, filename);
+ fd = create_object_tmpfile(&tmp_file, filename);
if (fd < 0) {
if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s", get_object_directory());
diff --git a/t/t0400-external-odb.sh b/t/t0400-external-odb.sh
new file mode 100755
index 0000000000..2b016173a0
--- /dev/null
+++ b/t/t0400-external-odb.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='basic tests for external object databases'
+
+. ./test-lib.sh
+
+ALT_SOURCE="$PWD/alt-repo/.git"
+export ALT_SOURCE
+write_script odb-helper <<\EOF
+GIT_DIR=$ALT_SOURCE; export GIT_DIR
+case "$1" in
+have)
+ git rev-list --all --objects |
+ cut -d' ' -f1 |
+ git cat-file --batch-check |
+ awk '{print $1 " " $3 " " $2}'
+ ;;
+get)
+ cat "$GIT_DIR"/objects/$(echo $2 | sed 's#..#&/#')
+ ;;
+esac
+EOF
+HELPER="\"$PWD\"/odb-helper"
+
+test_expect_success 'setup alternate repo' '
+ git init alt-repo &&
+ (cd alt-repo &&
+ test_commit one &&
+ test_commit two
+ ) &&
+ alt_head=`cd alt-repo && git rev-parse HEAD`
+'
+
+test_expect_success 'alt objects are missing' '
+ test_must_fail git log --format=%s $alt_head
+'
+
+test_expect_success 'helper can retrieve alt objects' '
+ test_config odb.magic.command "$HELPER" &&
+ cat >expect <<-\EOF &&
+ two
+ one
+ EOF
+ git log --format=%s $alt_head >actual &&
+ test_cmp expect actual
+'
+
+test_done
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* [RFC/PATCH v3 14/16] lib-httpd: add apache-e-odb.conf
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
This is an apache config file to test external object databases.
It uses the upload.sh and list.sh cgi that have been added
previously to make apache store external objects.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
t/lib-httpd/apache-e-odb.conf | 214 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 214 insertions(+)
create mode 100644 t/lib-httpd/apache-e-odb.conf
diff --git a/t/lib-httpd/apache-e-odb.conf b/t/lib-httpd/apache-e-odb.conf
new file mode 100644
index 0000000000..19a1540c82
--- /dev/null
+++ b/t/lib-httpd/apache-e-odb.conf
@@ -0,0 +1,214 @@
+ServerName dummy
+PidFile httpd.pid
+DocumentRoot www
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+CustomLog access.log common
+ErrorLog error.log
+<IfModule !mod_log_config.c>
+ LoadModule log_config_module modules/mod_log_config.so
+</IfModule>
+<IfModule !mod_alias.c>
+ LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_cgi.c>
+ LoadModule cgi_module modules/mod_cgi.so
+</IfModule>
+<IfModule !mod_env.c>
+ LoadModule env_module modules/mod_env.so
+</IfModule>
+<IfModule !mod_rewrite.c>
+ LoadModule rewrite_module modules/mod_rewrite.so
+</IFModule>
+<IfModule !mod_version.c>
+ LoadModule version_module modules/mod_version.so
+</IfModule>
+<IfModule !mod_headers.c>
+ LoadModule headers_module modules/mod_headers.so
+</IfModule>
+
+<IfVersion < 2.4>
+LockFile accept.lock
+</IfVersion>
+
+<IfVersion < 2.1>
+<IfModule !mod_auth.c>
+ LoadModule auth_module modules/mod_auth.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.1>
+<IfModule !mod_auth_basic.c>
+ LoadModule auth_basic_module modules/mod_auth_basic.so
+</IfModule>
+<IfModule !mod_authn_file.c>
+ LoadModule authn_file_module modules/mod_authn_file.so
+</IfModule>
+<IfModule !mod_authz_user.c>
+ LoadModule authz_user_module modules/mod_authz_user.so
+</IfModule>
+<IfModule !mod_authz_host.c>
+ LoadModule authz_host_module modules/mod_authz_host.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.4>
+<IfModule !mod_authn_core.c>
+ LoadModule authn_core_module modules/mod_authn_core.so
+</IfModule>
+<IfModule !mod_authz_core.c>
+ LoadModule authz_core_module modules/mod_authz_core.so
+</IfModule>
+<IfModule !mod_access_compat.c>
+ LoadModule access_compat_module modules/mod_access_compat.so
+</IfModule>
+<IfModule !mod_mpm_prefork.c>
+ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+<IfModule !mod_unixd.c>
+ LoadModule unixd_module modules/mod_unixd.so
+</IfModule>
+</IfVersion>
+
+PassEnv GIT_VALGRIND
+PassEnv GIT_VALGRIND_OPTIONS
+PassEnv GNUPGHOME
+PassEnv ASAN_OPTIONS
+PassEnv GIT_TRACE
+PassEnv GIT_CONFIG_NOSYSTEM
+
+Alias /dumb/ www/
+Alias /auth/dumb/ www/auth/dumb/
+
+<LocationMatch /smart/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
+<LocationMatch /smart_noexport/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+</LocationMatch>
+<LocationMatch /smart_custom_env/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_COMMITTER_NAME "Custom User"
+ SetEnv GIT_COMMITTER_EMAIL custom@example.com
+</LocationMatch>
+<LocationMatch /smart_namespace/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_NAMESPACE ns
+</LocationMatch>
+<LocationMatch /smart_cookies/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ Header set Set-Cookie name=value
+</LocationMatch>
+<LocationMatch /smart_headers/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
+ScriptAlias /upload/ upload.sh/
+ScriptAlias /list/ list.sh/
+<Directory ${GIT_EXEC_PATH}>
+ Options FollowSymlinks
+</Directory>
+<Files upload.sh>
+ Options ExecCGI
+</Files>
+<Files list.sh>
+ Options ExecCGI
+</Files>
+<Files ${GIT_EXEC_PATH}/git-http-backend>
+ Options ExecCGI
+</Files>
+
+RewriteEngine on
+RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
+RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
+RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
+RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301]
+RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
+
+RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
+RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
+
+# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
+# And as RewriteCond does not allow testing for non-matches, we match
+# the desired case first (one has abra, two has cadabra), and let it
+# pass by marking the RewriteRule as [L], "last rule, do not process
+# any other matching RewriteRules after this"), and then have another
+# RewriteRule that matches all other cases and lets them fail via '[F]',
+# "fail the request".
+RewriteCond %{HTTP:x-magic-one} =abra
+RewriteCond %{HTTP:x-magic-two} =cadabra
+RewriteRule ^/smart_headers/.* - [L]
+RewriteRule ^/smart_headers/.* - [F]
+
+<IfDefine SSL>
+LoadModule ssl_module modules/mod_ssl.so
+
+SSLCertificateFile httpd.pem
+SSLCertificateKeyFile httpd.pem
+SSLRandomSeed startup file:/dev/urandom 512
+SSLRandomSeed connect file:/dev/urandom 512
+SSLSessionCache none
+SSLMutex file:ssl_mutex
+SSLEngine On
+</IfDefine>
+
+<Location /auth/>
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</Location>
+
+<LocationMatch "^/auth-push/.*/git-receive-pack$">
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</LocationMatch>
+
+<LocationMatch "^/auth-fetch/.*/git-upload-pack$">
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</LocationMatch>
+
+RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
+RewriteCond %{REQUEST_URI} /git-receive-pack$
+RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
+
+<Location /half-auth-complete/>
+ Order Deny,Allow
+ Deny from env=AUTHREQUIRED
+
+ AuthType Basic
+ AuthName "Git Access"
+ AuthUserFile passwd
+ Require valid-user
+ Satisfy Any
+</Location>
+
+<IfDefine DAV>
+ LoadModule dav_module modules/mod_dav.so
+ LoadModule dav_fs_module modules/mod_dav_fs.so
+
+ DAVLockDB DAVLock
+ <Location /dumb/>
+ Dav on
+ </Location>
+ <Location /auth/dumb>
+ Dav on
+ </Location>
+</IfDefine>
+
+<IfDefine SVN>
+ LoadModule dav_svn_module modules/mod_dav_svn.so
+
+ <Location /${LIB_HTTPD_SVN}>
+ DAV svn
+ SVNPath "${LIB_HTTPD_SVNPATH}"
+ </Location>
+</IfDefine>
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* [RFC/PATCH v3 15/16] odb-helper: add 'store_plain_objects' to 'struct odb_helper'
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
This adds a configuration option odb.<helper>.plainObjects and the
corresponding boolean variable called 'store_plain_objects' in
'struct odb_helper' to make it possible for external object
databases to store object as plain objects instead of Git objects.
The existing odb_helper_fetch_object() is renamed
odb_helper_fetch_git_object() and a new odb_helper_fetch_plain_object()
is introduce to deal with external objects that are not in Git format.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
external-odb.c | 2 +
odb-helper.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
odb-helper.h | 1 +
3 files changed, 114 insertions(+), 2 deletions(-)
diff --git a/external-odb.c b/external-odb.c
index a980fbfbf2..af55377281 100644
--- a/external-odb.c
+++ b/external-odb.c
@@ -36,6 +36,8 @@ static int external_odb_config(const char *var, const char *value, void *data)
if (!strcmp(key, "command"))
return git_config_string(&o->cmd, var, value);
+ if (!strcmp(key, "plainobjects"))
+ o->store_plain_objects = git_config_bool(var, value);
return 0;
}
diff --git a/odb-helper.c b/odb-helper.c
index 7b7de7380f..6b9fb7927a 100644
--- a/odb-helper.c
+++ b/odb-helper.c
@@ -153,8 +153,107 @@ int odb_helper_has_object(struct odb_helper *o, const unsigned char *sha1)
return !!odb_helper_lookup(o, sha1);
}
-int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
- int fd)
+static int odb_helper_fetch_plain_object(struct odb_helper *o,
+ const unsigned char *sha1,
+ int fd)
+{
+ struct odb_helper_object *obj;
+ struct odb_helper_cmd cmd;
+ unsigned long total_got = 0;
+
+ char hdr[32];
+ int hdrlen;
+
+ int ret = Z_STREAM_END;
+ unsigned char compressed[4096];
+ git_zstream stream;
+ git_SHA_CTX hash;
+ unsigned char real_sha1[20];
+
+ obj = odb_helper_lookup(o, sha1);
+ if (!obj)
+ return -1;
+
+ if (odb_helper_start(o, &cmd, 0, "get %s", sha1_to_hex(sha1)) < 0)
+ return -1;
+
+ /* Set it up */
+ git_deflate_init(&stream, zlib_compression_level);
+ stream.next_out = compressed;
+ stream.avail_out = sizeof(compressed);
+ git_SHA1_Init(&hash);
+
+ /* First header.. */
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj->type), obj->size) + 1;
+ stream.next_in = (unsigned char *)hdr;
+ stream.avail_in = hdrlen;
+ while (git_deflate(&stream, 0) == Z_OK)
+ ; /* nothing */
+ git_SHA1_Update(&hash, hdr, hdrlen);
+
+ for (;;) {
+ unsigned char buf[4096];
+ int r;
+
+ r = xread(cmd.child.out, buf, sizeof(buf));
+ if (r < 0) {
+ error("unable to read from odb helper '%s': %s",
+ o->name, strerror(errno));
+ close(cmd.child.out);
+ odb_helper_finish(o, &cmd);
+ git_deflate_end(&stream);
+ return -1;
+ }
+ if (r == 0)
+ break;
+
+ total_got += r;
+
+ /* Then the data itself.. */
+ stream.next_in = (void *)buf;
+ stream.avail_in = r;
+ do {
+ unsigned char *in0 = stream.next_in;
+ ret = git_deflate(&stream, Z_FINISH);
+ git_SHA1_Update(&hash, in0, stream.next_in - in0);
+ write_or_die(fd, compressed, stream.next_out - compressed);
+ stream.next_out = compressed;
+ stream.avail_out = sizeof(compressed);
+ } while (ret == Z_OK);
+ }
+
+ close(cmd.child.out);
+ if (ret != Z_STREAM_END) {
+ warning("bad zlib data from odb helper '%s' for %s",
+ o->name, sha1_to_hex(sha1));
+ return -1;
+ }
+ ret = git_deflate_end_gently(&stream);
+ if (ret != Z_OK) {
+ warning("deflateEnd on object %s from odb helper '%s' failed (%d)",
+ sha1_to_hex(sha1), o->name, ret);
+ return -1;
+ }
+ git_SHA1_Final(real_sha1, &hash);
+ if (hashcmp(sha1, real_sha1)) {
+ warning("sha1 mismatch from odb helper '%s' for %s (got %s)",
+ o->name, sha1_to_hex(sha1), sha1_to_hex(real_sha1));
+ return -1;
+ }
+ if (odb_helper_finish(o, &cmd))
+ return -1;
+ if (total_got != obj->size) {
+ warning("size mismatch from odb helper '%s' for %s (%lu != %lu)",
+ o->name, sha1_to_hex(sha1), total_got, obj->size);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int odb_helper_fetch_git_object(struct odb_helper *o,
+ const unsigned char *sha1,
+ int fd)
{
struct odb_helper_object *obj;
struct odb_helper_cmd cmd;
@@ -242,6 +341,16 @@ int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
return 0;
}
+int odb_helper_fetch_object(struct odb_helper *o,
+ const unsigned char *sha1,
+ int fd)
+{
+ if (o->store_plain_objects)
+ return odb_helper_fetch_plain_object(o, sha1, fd);
+ else
+ return odb_helper_fetch_git_object(o, sha1, fd);
+}
+
int odb_helper_for_each_object(struct odb_helper *o,
each_external_object_fn fn,
void *data)
diff --git a/odb-helper.h b/odb-helper.h
index af31cc27d5..80d332139d 100644
--- a/odb-helper.h
+++ b/odb-helper.h
@@ -6,6 +6,7 @@
struct odb_helper {
const char *name;
const char *cmd;
+ int store_plain_objects;
struct odb_helper_object {
unsigned char sha1[20];
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* [RFC/PATCH v3 16/16] t0420: add test with HTTP external odb
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
This tests that an apache web server can be used as an
external object database and store files in their native
format instead of converting them to a Git object.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
t/t0420-transfer-http-e-odb.sh | 118 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
create mode 100755 t/t0420-transfer-http-e-odb.sh
diff --git a/t/t0420-transfer-http-e-odb.sh b/t/t0420-transfer-http-e-odb.sh
new file mode 100755
index 0000000000..9fb84877b5
--- /dev/null
+++ b/t/t0420-transfer-http-e-odb.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='tests for transfering external objects to an HTTPD server'
+
+. ./test-lib.sh
+
+# If we don't specify a port, the current test number will be used
+# which will not work as it is less than 1024, so it can only be used by root.
+LIB_HTTPD_PORT=$(expr ${this_test#t} + 12000)
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+start_httpd apache-e-odb.conf
+
+# odb helper script must see this
+export HTTPD_URL
+
+write_script odb-http-helper <<\EOF
+die() {
+ printf >&2 "%s\n" "$@"
+ exit 1
+}
+echo >&2 "odb-http-helper args:" "$@"
+case "$1" in
+have)
+ list_url="$HTTPD_URL/list/"
+ curl "$list_url" ||
+ die "curl '$list_url' failed"
+ ;;
+get)
+ get_url="$HTTPD_URL/list/?sha1=$2"
+ curl "$get_url" ||
+ die "curl '$get_url' failed"
+ ;;
+put)
+ sha1="$2"
+ size="$3"
+ kind="$4"
+ upload_url="$HTTPD_URL/upload/?sha1=$sha1&size=$size&type=$kind"
+ curl --data-binary @- --include "$upload_url" >out ||
+ die "curl '$upload_url' failed"
+ ref_hash=$(echo "$sha1 $size $kind" | GIT_NO_EXTERNAL_ODB=1 git hash-object -w -t blob --stdin) || exit
+ git update-ref refs/odbs/magic/"$sha1" "$ref_hash"
+ ;;
+*)
+ die "unknown command '$1'"
+ ;;
+esac
+EOF
+HELPER="\"$PWD\"/odb-http-helper"
+
+
+test_expect_success 'setup repo with a root commit and the helper' '
+ test_commit zero &&
+ git config odb.magic.command "$HELPER" &&
+ git config odb.magic.plainObjects "true"
+'
+
+test_expect_success 'setup another repo from the first one' '
+ git init other-repo &&
+ (cd other-repo &&
+ git remote add origin .. &&
+ git pull origin master &&
+ git checkout master &&
+ git log)
+'
+
+UPLOADFILENAME="hello_apache_upload.txt"
+
+UPLOAD_URL="$HTTPD_URL/upload/?sha1=$UPLOADFILENAME&size=123&type=blob"
+
+test_expect_success 'can upload a file' '
+ echo "Hello Apache World!" >hello_to_send.txt &&
+ echo "How are you?" >>hello_to_send.txt &&
+ curl --data-binary @hello_to_send.txt --include "$UPLOAD_URL" >out_upload
+'
+
+LIST_URL="$HTTPD_URL/list/"
+
+test_expect_success 'can list uploaded files' '
+ curl --include "$LIST_URL" >out_list &&
+ grep "$UPLOADFILENAME" out_list
+'
+
+test_expect_success 'can delete uploaded files' '
+ curl --data "delete" --include "$UPLOAD_URL&delete=1" >out_delete &&
+ curl --include "$LIST_URL" >out_list2 &&
+ ! grep "$UPLOADFILENAME" out_list2
+'
+
+FILES_DIR="httpd/www/files"
+
+test_expect_success 'new blobs are transfered to the http server' '
+ test_commit one &&
+ hash1=$(git ls-tree HEAD | grep one.t | cut -f1 | cut -d\ -f3) &&
+ echo "$hash1-4-blob" >expected &&
+ ls "$FILES_DIR" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'blobs can be retrieved from the http server' '
+ git cat-file blob "$hash1" &&
+ git log -p >expected
+'
+
+test_expect_success 'update other repo from the first one' '
+ (cd other-repo &&
+ git fetch origin "refs/odbs/magic/*:refs/odbs/magic/*" &&
+ test_must_fail git cat-file blob "$hash1" &&
+ git config odb.magic.command "$HELPER" &&
+ git config odb.magic.plainObjects "true" &&
+ git cat-file blob "$hash1" &&
+ git pull origin master)
+'
+
+stop_httpd
+
+test_done
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* [RFC/PATCH v3 06/16] external odb: add write support
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
external-odb.c | 15 +++++++++++++++
external-odb.h | 2 ++
odb-helper.c | 41 +++++++++++++++++++++++++++++++++++++----
odb-helper.h | 3 +++
sha1_file.c | 2 ++
5 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/external-odb.c b/external-odb.c
index 42978a3298..bb70fe3298 100644
--- a/external-odb.c
+++ b/external-odb.c
@@ -127,3 +127,18 @@ int external_odb_for_each_object(each_external_object_fn fn, void *data)
}
return 0;
}
+
+int external_odb_write_object(const void *buf, unsigned long len,
+ const char *type, unsigned char *sha1)
+{
+ struct odb_helper *o;
+
+ external_odb_init();
+
+ for (o = helpers; o; o = o->next) {
+ int r = odb_helper_write_object(o, buf, len, type, sha1);
+ if (r <= 0)
+ return r;
+ }
+ return 1;
+}
diff --git a/external-odb.h b/external-odb.h
index cea8570a49..55d291d1cf 100644
--- a/external-odb.h
+++ b/external-odb.h
@@ -10,5 +10,7 @@ typedef int (*each_external_object_fn)(const unsigned char *sha1,
unsigned long size,
void *data);
int external_odb_for_each_object(each_external_object_fn, void *);
+int external_odb_write_object(const void *buf, unsigned long len,
+ const char *type, unsigned char *sha1);
#endif /* EXTERNAL_ODB_H */
diff --git a/odb-helper.c b/odb-helper.c
index 2db59caa53..7b7de7380f 100644
--- a/odb-helper.c
+++ b/odb-helper.c
@@ -33,9 +33,10 @@ static void prepare_helper_command(struct argv_array *argv, const char *cmd,
strbuf_release(&buf);
}
-__attribute__((format (printf,3,4)))
+__attribute__((format (printf,4,5)))
static int odb_helper_start(struct odb_helper *o,
struct odb_helper_cmd *cmd,
+ int use_stdin,
const char *fmt, ...)
{
va_list ap;
@@ -52,7 +53,10 @@ static int odb_helper_start(struct odb_helper *o,
cmd->child.argv = cmd->argv.argv;
cmd->child.use_shell = 1;
- cmd->child.no_stdin = 1;
+ if (use_stdin)
+ cmd->child.in = -1;
+ else
+ cmd->child.no_stdin = 1;
cmd->child.out = -1;
if (start_command(&cmd->child) < 0) {
@@ -109,7 +113,7 @@ static void odb_helper_load_have(struct odb_helper *o)
return;
o->have_valid = 1;
- if (odb_helper_start(o, &cmd, "have") < 0)
+ if (odb_helper_start(o, &cmd, 0, "have") < 0)
return;
fh = xfdopen(cmd.child.out, "r");
@@ -164,7 +168,7 @@ int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
if (!obj)
return -1;
- if (odb_helper_start(o, &cmd, "get %s", sha1_to_hex(sha1)) < 0)
+ if (odb_helper_start(o, &cmd, 0, "get %s", sha1_to_hex(sha1)) < 0)
return -1;
memset(&stream, 0, sizeof(stream));
@@ -252,3 +256,32 @@ int odb_helper_for_each_object(struct odb_helper *o,
return 0;
}
+
+int odb_helper_write_object(struct odb_helper *o,
+ const void *buf, unsigned long len,
+ const char *type, unsigned char *sha1)
+{
+ struct odb_helper_cmd cmd;
+
+ if (odb_helper_start(o, &cmd, 1, "put %s %lu %s",
+ sha1_to_hex(sha1), len, type) < 0)
+ return -1;
+
+ do {
+ int w = xwrite(cmd.child.in, buf, len);
+ if (w < 0) {
+ error("unable to write to odb helper '%s': %s",
+ o->name, strerror(errno));
+ close(cmd.child.in);
+ close(cmd.child.out);
+ odb_helper_finish(o, &cmd);
+ return -1;
+ }
+ len -= w;
+ } while (len > 0);
+
+ close(cmd.child.in);
+ close(cmd.child.out);
+ odb_helper_finish(o, &cmd);
+ return 0;
+}
diff --git a/odb-helper.h b/odb-helper.h
index 8c3916d215..af31cc27d5 100644
--- a/odb-helper.h
+++ b/odb-helper.h
@@ -25,5 +25,8 @@ int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
int fd);
int odb_helper_for_each_object(struct odb_helper *o,
each_external_object_fn, void *);
+int odb_helper_write_object(struct odb_helper *o,
+ const void *buf, unsigned long len,
+ const char *type, unsigned char *sha1);
#endif /* ODB_HELPER_H */
diff --git a/sha1_file.c b/sha1_file.c
index 6d68157e30..3532c1c598 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3320,6 +3320,8 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
* it out into .git/objects/??/?{38} file.
*/
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+ if (!external_odb_write_object(buf, len, type, sha1))
+ return 0;
if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
return 0;
return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* [RFC/PATCH v3 00/16] Add initial experimental external ODB support
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
Goal
~~~~
Git can store its objects only in the form of loose objects in
separate files or packed objects in a pack file.
To be able to better handle some kind of objects, for example big
blobs, it would be nice if Git could store its objects in other object
databases (ODB).
To do that, this patch series makes it possible to register commands,
using "odb.<odbname>.command" config variables, to access external
ODBs where objects can be stored and retrieved.
External ODBs should be able to tranfer information about the blobs
they store. This patch series shows how this is possible using kind of
replace refs.
Design
~~~~~~
* Registered command
Each registered command manages access to one external ODB and will be
called the following ways:
- "<command> have": the command should output the sha1, size and
type of all the objects the external ODB contains, one object per
line.
- "<command> get <sha1>": the command should then read from the
external ODB the content of the object corresponding to <sha1> and
output it on stdout.
- "<command> put <sha1> <size> <type>": the command should then read
from stdin an object and store it in the external ODB.
* Transfer
To tranfer information about the blobs stored in external ODB, some
special refs, called "odb ref", similar as replace refs, are used.
For now there should be one odb ref per blob. Each ref name should be
refs/odbs/<odbname>/<sha1> where <sha1> is the sha1 of the blob stored
in the external odb named <odbname>.
These odb refs should all point to a blob that should be stored in the
Git repository and contain information about the blob stored in the
external odb. This information can be specific to the external odb.
The repos can then share this information using commands like:
`git fetch origin "refs/odbs/<odbname>/*:refs/odbs/<odbname>/*"`
* External object database
This RFC patch series shows in the tests:
- how to use another git repository as an external ODB
- how to use an http server as an external ODB
Design discussion about performance
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yeah, it is not efficient to fork/exec a command to just read or write
one object to or from the external ODB. Batch calls and/or using a
daemon and/or RPC should be used instead to be able to store regular
objects in an external ODB. But for now the external ODB would be all
about really big files, where the cost of a fork+exec should not
matter much. If we later want to extend usage of external ODBs, yeah
we will probably need to design other mechanisms.
Here are some related explanations from Peff:
{{{
Because this "external odb" essentially acts as a git alternate, we
would hit it only when we couldn't find an object through regular means.
Git would then make the object available in the usual on-disk format
(probably as a loose object).
So in most processes, we would not need to consult the odb command at
all. And when we do, the first thing would be to get its "have" list,
which would at most run once per process.
So the per-object cost is really calling "get", and my assumption there
was that the cost of actually retrieving the object over the network
would dwarf the fork/exec cost.
I also waffled on having git cache the output of "<command> have" in
some fast-lookup format to save even the single fork/exec. But I figured
that was something that could be added later if needed.
You'll note that this is sort of a "fault-in" model. Another model would
be to treat external odb updates similar to fetches. I.e., we touch the
network only during a special update operation, and then try to work
locally with whatever the external odb has. IMHO this policy could
actually be up to the external odb itself (i.e., its "have" command
could serve from a local cache if it likes).
}}}
Implementation
~~~~~~~~~~~~~~
* Mechanism to call the registered commands
This series adds a set of function in external-odb.{c,h} that are
called by the rest of Git to manage all the external ODBs.
These functions use 'struct odb_helper' and its associated functions
defined in odb-helper.{c,h} to talk to the different external ODBs by
launching the configured "odb.<odbname>.command" commands and writing
to or reading from them.
The tests in this series creates an odb-helper script that is
registered using the "odb.magic.command" config variable, and then
called to read from and write to the external ODB.
* ODB refs
For now odb ref management is only implemented in a registered command
in t0410, but maybe this or some parts of it could be done by Git
itself.
When a new blob is added to an external odb, its sha1, size and type
are writen in another new blob and the odb ref is created.
When the list of existing blobs is requested from the external odb,
the content of the blobs pointed to by the odb refs can also be used
by the odb to claim that it can get the objects.
When a blob is actually requested from the external odb, it can use
the content stored in the blobs pointed to by the odb refs to get the
actual blobs and then pass them.
Highlevel view of the patches in the series
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Patches 01/16 and 02/16 are Peff's initial work. They are not
changed since v1.
- Patches 03/16 is an optimization in the odb-helper script that
is used for testing. I will probably squash it into 01/08, but
didn't yet. So there is no change since v1.
- Patches 04/16 and 05/16 are adding "put" support in the
odb-helper script and testing that. They are not changed since
v1.
- Patches 06/16 and 08/16 are enhancing external-odb.{c,h} and
odb-helper.{c,h}, so that Git can write into an external
ODB. They are not changed since v1.
- Patch 07/16 limits write support to "blobs" for now to
simplify things. It did not change since v1.
- Patch 09/16 adds a GIT_NO_EXTERNAL_ODB env variable to disable
using the external database. It was new in v2.
- Patch 10/16 adds test t0410 that shows how odb refs can be used
to transfer information about blobs managed by an external
odb. It was new in v2.
- Patches 11/16 to 14/16 are preparing cgi and a apache config
file so that an apache server can be used as an external object
database. It is based on existing infrastructure in t/lib-http/.
This is new in v3.
- Patch 15/16 adds support for external ODBs that are storing
files in their original format instead of as Git objects. It
adds the odb.<helper>.plainObject config option to support these
external ODBs. This is new in v3.
- Patch 16/16 adds test t0420 that shows how an apache server can be used
as an external ODB. This is new in v3.
Future work
~~~~~~~~~~~
I think that the odb refs don't prevent a regular fetch or push from
wanting to send the objects that are managed by an external odb. So I
am interested in suggestions about this problem. I will take a look at
previous discussions and how other mechanisms (shallow clone, bundle
v3, ...) handle this.
One interesting thing also would be to use the streaming api when
reading from or writing to the external ODB. (If it is not
automatically used already when the blob is bigger than
core.bigFileThreshold.)
Previous work and discussions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Sorry for the old Gmane links, I will try to replace them with
public-inbox.org at one point.)
Peff started to work on this and discuss this some years ago:
http://thread.gmane.org/gmane.comp.version-control.git/206886/focus=207040
http://thread.gmane.org/gmane.comp.version-control.git/247171
http://thread.gmane.org/gmane.comp.version-control.git/202902/focus=203020
His work, which is not compile-tested any more, is still there:
https://github.com/peff/git/commits/jk/external-odb-wip
Initial discussions about this new series are there:
http://thread.gmane.org/gmane.comp.version-control.git/288151/focus=295160
Version 1 and 2 of this RFC/PATCH series are here:
https://public-inbox.org/git/20160613085546.11784-1-chriscool@tuxfamily.org/
https://public-inbox.org/git/20160628181933.24620-1-chriscool@tuxfamily.org/
Links
~~~~~
This patch series is available here:
https://github.com/chriscool/git/commits/external-odb
Version 1 and 2 are here:
https://github.com/chriscool/git/commits/gl-external-odb12
https://github.com/chriscool/git/commits/gl-external-odb22
Christian Couder (14):
t0400: use --batch-all-objects to get all objects
t0400: add 'put' command to odb-helper script
t0400: add test for 'put' command
external odb: add write support
external-odb: accept only blobs for now
t0400: add test for external odb write support
Add GIT_NO_EXTERNAL_ODB env variable
Add t0410 to test external ODB transfer
lib-httpd: pass config file to start_httpd()
lib-httpd: add upload.sh
lib-httpd: add list.sh
lib-httpd: add apache-e-odb.conf
odb-helper: add 'store_plain_objects' to 'struct odb_helper'
t0420: add test with HTTP external odb
Jeff King (2):
Add initial external odb support
external odb foreach
Makefile | 2 +
cache.h | 18 ++
environment.c | 4 +
external-odb.c | 156 ++++++++++++++++
external-odb.h | 16 ++
odb-helper.c | 396 +++++++++++++++++++++++++++++++++++++++++
odb-helper.h | 33 ++++
sha1_file.c | 69 +++++--
t/lib-httpd.sh | 8 +-
t/lib-httpd/apache-e-odb.conf | 214 ++++++++++++++++++++++
t/lib-httpd/list.sh | 34 ++++
t/lib-httpd/upload.sh | 45 +++++
t/t0400-external-odb.sh | 77 ++++++++
t/t0410-transfer-e-odb.sh | 136 ++++++++++++++
t/t0420-transfer-http-e-odb.sh | 118 ++++++++++++
15 files changed, 1309 insertions(+), 17 deletions(-)
create mode 100644 external-odb.c
create mode 100644 external-odb.h
create mode 100644 odb-helper.c
create mode 100644 odb-helper.h
create mode 100644 t/lib-httpd/apache-e-odb.conf
create mode 100644 t/lib-httpd/list.sh
create mode 100644 t/lib-httpd/upload.sh
create mode 100755 t/t0400-external-odb.sh
create mode 100755 t/t0410-transfer-e-odb.sh
create mode 100755 t/t0420-transfer-http-e-odb.sh
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply
* [RFC/PATCH v3 05/16] t0400: add test for 'put' command
From: Christian Couder @ 2016-11-30 21:04 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Lars Schneider, Eric Wong, Christian Couder
In-Reply-To: <20161130210420.15982-1-chriscool@tuxfamily.org>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
t/t0400-external-odb.sh | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/t/t0400-external-odb.sh b/t/t0400-external-odb.sh
index 0f1bb97f38..6c6da5cf4f 100755
--- a/t/t0400-external-odb.sh
+++ b/t/t0400-external-odb.sh
@@ -57,4 +57,13 @@ test_expect_success 'helper can retrieve alt objects' '
test_cmp expect actual
'
+test_expect_success 'helper can add objects to alt repo' '
+ hash=$(echo "Hello odb!" | git hash-object -w -t blob --stdin) &&
+ test -f .git/objects/$(echo $hash | sed "s#..#&/#") &&
+ size=$(git cat-file -s "$hash") &&
+ git cat-file blob "$hash" | ./odb-helper put "$hash" "$size" blob &&
+ alt_size=$(cd alt-repo && git cat-file -s "$hash") &&
+ test "$size" -eq "$alt_size"
+'
+
test_done
--
2.11.0.rc2.37.geb49ca6
^ permalink raw reply related
* Re: [PATCHv2 4/4] submodule: add embed-git-dir function
From: Junio C Hamano @ 2016-11-30 20:51 UTC (permalink / raw)
To: Duy Nguyen
Cc: Stefan Beller, Brandon Williams, Git Mailing List,
Jonathan Nieder, Jens Lehmann, Heiko Voigt
In-Reply-To: <CACsJy8Ce3Oa-xJ4BwgRRy6neM=Jxkfqq7yboHZDXLDG2tu9GzQ@mail.gmail.com>
Duy Nguyen <pclouds@gmail.com> writes:
> On Wed, Nov 23, 2016 at 2:22 AM, Stefan Beller <sbeller@google.com> wrote:
>> +/*
>> + * Migrate the given submodule (and all its submodules recursively) from
>> + * having its git directory within the working tree to the git dir nested
>> + * in its superprojects git dir under modules/.
>> + */
>> +void migrate_submodule_gitdir(const char *prefix, const char *path,
>> + int recursive)
>
> Submodules and worktrees seem to have many things in common. The first
> one is this. "git worktree move" on a worktree that contains
> submodules .git also benefits from something like this [1]. I suggest
> you move this function to some neutral place and maybe rename it to
> relocate_gitdir() or something.
Yeah, good suggestion (including name; first round used "intern" I
had trouble with, then "embed" which was OK-ish, but probably
"relocate" is better choice. If anything, what Stefan's series adds
is a command to un-embed embedded one).
> It probably should take a bit flag instead of "recursive" here. One
> thing I would need is the ability to tell this function "I have moved
> all these .git dirs already (because I move whole worktree in one
> operation), here are the old and new locations of them, fix them up!".
> In other words, no rename() could be optionally skipped.
Thanks two of you for working well together ;-)
^ permalink raw reply
* Re: [PATCHv2 4/4] submodule: add embed-git-dir function
From: Stefan Beller @ 2016-11-30 20:48 UTC (permalink / raw)
To: Duy Nguyen
Cc: Brandon Williams, Junio C Hamano, Git Mailing List,
Jonathan Nieder, Jens Lehmann, Heiko Voigt
In-Reply-To: <CAGZ79kbvey_f8+R16yYT_qsF0RErOh8own8n-RRApTM0dS-+ag@mail.gmail.com>
On Wed, Nov 30, 2016 at 10:04 AM, Stefan Beller <sbeller@google.com> wrote:
>> Submodules and worktrees seem to have many things in common.
>
> Yes. :)
I moved the code to dir.{c,h} and renamed it to a more generic "move_gitdir",
but then I am thinking further about how to align worktrees and submodules here,
specifically the recursive part.
Whenever submodules and recursion is involved so far we spawned off a child
process to take care of the issue in the submodule itself. Here we followed
the same pattern and called the submodule--helper to embed the git dirs
in the submodule recursively.
As this function is not supposed to be submodule specific anymore, I'd
rather not want to have the recursive childrens code live inside the submodule
helper, so we also need a neutral helper for moving git directories?
So there are a couple ways forward:
* We declare the "recursive" flag to be submodule specific and keep
the recursion
in the submodule helper
* the recursive flag is generic enough and we invent a plumbing helper.
Analogous to the submodule--helper, that was originally invented as a C aid
for the git-submodule shell script, we could have a "git--helper" or just
"git plumbing <subcmd>", though that sounds like reinventing the wheel as
traditionally we'd just have a top level command to do a specific thing.
(side note: Some parts of git-rev-parse could go into such a plumbing
command)
For now I'd think to declare recursion in this function to be
submodule specific.
Stefan
^ permalink raw reply
* Re: [PATCHv2 4/4] submodule: add embed-git-dir function
From: Stefan Beller @ 2016-11-30 21:00 UTC (permalink / raw)
To: Junio C Hamano
Cc: Duy Nguyen, Brandon Williams, Git Mailing List, Jonathan Nieder,
Jens Lehmann, Heiko Voigt
In-Reply-To: <xmqqpolcd73b.fsf@gitster.mtv.corp.google.com>
On Wed, Nov 30, 2016 at 12:51 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Duy Nguyen <pclouds@gmail.com> writes:
>
>> On Wed, Nov 23, 2016 at 2:22 AM, Stefan Beller <sbeller@google.com> wrote:
>>> +/*
>>> + * Migrate the given submodule (and all its submodules recursively) from
>>> + * having its git directory within the working tree to the git dir nested
>>> + * in its superprojects git dir under modules/.
>>> + */
>>> +void migrate_submodule_gitdir(const char *prefix, const char *path,
>>> + int recursive)
>>
>> Submodules and worktrees seem to have many things in common. The first
>> one is this. "git worktree move" on a worktree that contains
>> submodules .git also benefits from something like this [1]. I suggest
>> you move this function to some neutral place and maybe rename it to
>> relocate_gitdir() or something.
>
> Yeah, good suggestion (including name; first round used "intern" I
> had trouble with, then "embed" which was OK-ish, but probably
> "relocate" is better choice. If anything, what Stefan's series adds
> is a command to un-embed embedded one).
Heh, good counter perspective. I thought about embedding it
"into the superprojects git directory" whereas your un-embed sounds
like you'd assume embedding is targetd towards the working dir.
relocate as a fancy name for move sounds like it expects 2 arguments
(source and destination), but maybe we could go with:
git relocate-git-dir (--into-workingtree|--into-gitdir) \
[--recurse-submodules] \
[--only-fix-gitfile-links-and-core-setting-as-I-moved-it-myself-already] \
[--dryrun] [--verbose] [--unsafe-move]
No need to have a pathspec here IMHO. Later it could have another flag
to specify the "main" worktree or such as well.
^ permalink raw reply
* Re: [PATCH] difftool.c: mark a file-local symbol with static
From: Junio C Hamano @ 2016-11-30 20:40 UTC (permalink / raw)
To: Ramsay Jones; +Cc: Johannes Schindelin, GIT Mailing-list
In-Reply-To: <29abc89b-9ca5-930f-8e90-ca446ac2b96a@ramsayjones.plus.com>
Ramsay Jones <ramsay@ramsayjones.plus.com> writes:
> [I have fixed my config.mak file now, so I don't see the warning
> anymore! Having -Wno-format-zero-length in DEVELOPER_CFLAGS, or
> not, is a separate matter.]
I suspect that 658df95a4a ("add DEVELOPER makefile knob to check for
acknowledged warnings", 2016-02-25) took it from me (namely, Make
script in my 'todo' branch). In turn, I added it to my set of flags
in order to squelch this exact warning, so...
^ permalink raw reply
* "git add -p ." raises an unexpected "warning: empty strings as pathspecs will be made invalid in upcoming releases. please use . instead if you meant to match all paths"
From: Peter Urda @ 2016-11-30 20:31 UTC (permalink / raw)
To: git
After upgrading to version 2.11.0 I am getting a warning about empty
strings as pathspecs while using 'patch'
- Ran 'git add -p .' from the root of my git repository.
- I was able to normally stage my changes, but was presented with a
"warning: empty strings as pathspecs will be made invalid in upcoming
releases. please use . instead if you meant to match all paths"
message.
- I expected no warning message since I included a "." with my original command.
I believe that I should not be seeing this warning message as I
included the requested "." pathspec.
~ Peter Urda
http://urda.cc
^ permalink raw reply
* Re: [PATCH] difftool.c: mark a file-local symbol with static
From: Ramsay Jones @ 2016-11-30 19:57 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, GIT Mailing-list
In-Reply-To: <alpine.DEB.2.20.1611301204020.117539@virtualbox>
On 30/11/16 11:07, Johannes Schindelin wrote:
> Hi Ramsay,
>
> On Tue, 29 Nov 2016, Ramsay Jones wrote:
>> Also, due to a problem in my config.mak file on Linux (a commented
>> out line that had a line continuation '\', grrrrr!), gcc issued a
>> warning, thus:
>>
>> builtin/difftool.c: In function ‘run_dir_diff’:
>> builtin/difftool.c:568:13: warning: zero-length gnu_printf format string [-Wformat-zero-length]
>> warning("");
>> ^
>> I am not sure why -Wno-format-zero-length is set in DEVELOPER_CFLAGS,
>> but do you really need to space the output with an an 'empty'
>> "warning:" line? (Just curious).
>
> That `warning("");` comes from a straight-forward port of this line (see
> https://github.com/git/git/blob/v2.11.0/git-difftool.perl#L425):
>
> $errmsg .= "warning:\n";
Ah, OK, so it is being used to 'space out' the (possibly multiple)
'Both files modified' warning(s) followed by a 2-line warning
summary. Hmm, I am not sure the 'Working tree file has been left'
(after each pair of files) part of the message is adding much ...
> I could see two possible ways out:
>
> - warning("%s", ""); (ugly!)
>
> - do away with the "prefix every line with warning:" convention and simply
> have a multi-line `warning(_("...\n...\n"), ...)`
>
> What do you think?
I think, for now, it is probably best to do nothing. ;-)
Until you have replaced the 'legacy' difftool, it would be best
not to alter the output from the tool, so that the tests can be
used unaltered on both. This could be addressed (if necessary)
after you complete your series.
[I have fixed my config.mak file now, so I don't see the warning
anymore! Having -Wno-format-zero-length in DEVELOPER_CFLAGS, or
not, is a separate matter.]
ATB,
Ramsay Jones
^ permalink raw reply
* Re: What's cooking in git.git (Nov 2016, #06; Mon, 28)
From: Brandon Williams @ 2016-11-30 19:54 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20161129065125.cwlbkctniy7oshj2@sigill.intra.peff.net>
On 11/29, Jeff King wrote:
> On Tue, Nov 29, 2016 at 01:37:59AM -0500, Jeff King wrote:
>
> > 2. Grep threads doing more complicated stuff that needs to take a
> > lock. You might try building with -fsanitize=thread to see if it
> > turns up anything.
>
> I tried this and it didn't find anything useful. It complains about
> multiple threads calling want_color() at the same time, which you can
> silence with something like:
>
> diff --git a/builtin/grep.c b/builtin/grep.c
> index 2c727ef49..d48846f40 100644
> --- a/builtin/grep.c
> +++ b/builtin/grep.c
> @@ -207,6 +207,12 @@ static void start_threads(struct grep_opt *opt)
> {
> int i;
>
> + /*
> + * trigger want_color() for its side effect of caching the result;
> + * otherwise the threads will fight over setting the cache
> + */
> + want_color(GIT_COLOR_AUTO);
> +
> pthread_mutex_init(&grep_mutex, NULL);
> pthread_mutex_init(&grep_read_mutex, NULL);
> pthread_mutex_init(&grep_attr_mutex, NULL);
>
> But the problem persists even with that patch, so it is something else.
> It may still be a threading problem; -fsanitize=thread isn't perfect. I
> also couldn't get the stress-test to fail when compiled with it. But
> that may simply mean that the timing of the resulting binary is changed
> enough not to trigger the issue.
>
> -Peff
With you're stress script I'm able to see the failures. The interesting
thing is that the entry missing is always from the non-submodule file.
--
Brandon Williams
^ permalink raw reply
* Re: gitconfig includes
From: Eli Barzilay @ 2016-11-30 19:42 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20161130190653.kk5pboas54yen2it@sigill.intra.peff.net>
On Wed, Nov 30, 2016 at 2:06 PM, Jeff King <peff@peff.net> wrote:
>
> I'm not sure what your script does exactly, but in general I think the
> right thing for most scripts is _not_ to use a specific-file option
> like --global.
>
> If the script is looking up a config value on behalf of a user, it
> probably makes sense for it to use the normal config lookup procedure
> (system, global, repo, command-line), which also enables includes by
> default. That would make it consistent with internal git config
> lookups (e.g., user.name probably only ever appears in global config,
> but you _can_ override it at the repo level if you want to).
This is intended for git newbies (and big company => infinite supply of
them), and also allows them to conveniently nuke the repo and start from
a fresh copy, so it makes sense to make the script inspect/tweak the
global settings. If knowing git "well enough" was an assumed
requirement, I'd definitely do the normal thing.
> I know that's mostly orthogonal to what we're discussing, but I'd feel
> more convinced that enabling "--includes" with "--global" is useful if
> I thought that "--global" was useful in the first place outside of a
> few narrow debugging cases.
Ok. Perhaps I overestimated the utility of --global anyway, given the
above...
--
((x=>x(x))(x=>x(x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!
^ permalink raw reply
* Re: gitconfig includes
From: Jeff King @ 2016-11-30 19:06 UTC (permalink / raw)
To: Eli Barzilay; +Cc: Junio C Hamano, git
In-Reply-To: <CALO-gusHzTaLg=7X=KqYB==Yz_6yH6qkh8GDK54Lacu5ofD2pw@mail.gmail.com>
On Wed, Nov 30, 2016 at 01:54:35PM -0500, Eli Barzilay wrote:
> I don't have any strong opinion, but FWIW, the use case I have for this
> is as follows: I sync my ~/.gitconfig between my own machine and a work
> machine. On the work machine though, I like people to have work emails,
> and I wrote some scripts that verify that. For my case, I added an
> include of a ~/.gitconfig.more which is not synced, and has values that
> override the ones in ~/.gitconfig. Since I'm the one who also wrote
> that script, I just added an "--includes" to the check so it won't barf
> on my setup, but had it not been my script I'd be stuck.
I'm not sure what your script does exactly, but in general I think the
right thing for most scripts is _not_ to use a specific-file option like
--global.
If the script is looking up a config value on behalf of a user, it
probably makes sense for it to use the normal config lookup procedure
(system, global, repo, command-line), which also enables includes by
default. That would make it consistent with internal git config lookups
(e.g., user.name probably only ever appears in global config, but you
_can_ override it at the repo level if you want to).
I know that's mostly orthogonal to what we're discussing, but I'd feel
more convinced that enabling "--includes" with "--global" is useful if I
thought that "--global" was useful in the first place outside of a few
narrow debugging cases.
-Peff
^ permalink raw reply
* Re: gitconfig includes
From: Eli Barzilay @ 2016-11-30 18:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git
In-Reply-To: <xmqqeg1udkg4.fsf@gitster.mtv.corp.google.com>
On Tue, Nov 29, 2016 at 4:50 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Jeff King <peff@peff.net> writes:
>
>> I think it's arguable whether "--global" should behave the same.
>
> I know you know this and I am writing this message for others.
>
> I admit that I wondered if "a single file" ought to cover these
> short-hand notations like --global and --local while re-reading the
> log message of 9b25a0b52 (config: add include directive,
> 2012-02-06). In other words, I agree that it used to be arguable
> before we released v1.7.10.
>
> It no longer is arguable simply due to backward compatibilty. The
> ship has long sailed.
I don't have any strong opinion, but FWIW, the use case I have for this
is as follows: I sync my ~/.gitconfig between my own machine and a work
machine. On the work machine though, I like people to have work emails,
and I wrote some scripts that verify that. For my case, I added an
include of a ~/.gitconfig.more which is not synced, and has values that
override the ones in ~/.gitconfig. Since I'm the one who also wrote
that script, I just added an "--includes" to the check so it won't barf
on my setup, but had it not been my script I'd be stuck.
This is all a "FWIW" -- in case anyone thinks about use cases for a
possible (future) change of the default.
--
((x=>x(x))(x=>x(x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!
^ permalink raw reply
* Re: [PATCH v3 1/2] difftool: add a skeleton for the upcoming builtin
From: Junio C Hamano @ 2016-11-30 18:39 UTC (permalink / raw)
To: Jakub Narębski
Cc: Johannes Schindelin, Jeff King, git, David Aguilar,
Dennis Kaarsemaker
In-Reply-To: <855b9172-7225-e09e-e46d-87940f9fda75@gmail.com>
Jakub Narębski <jnareb@gmail.com> writes:
>> My original "create a file in libexec/git-core/" was simple, did the job
>> reliably, and worked also for testing.
>>
>> It is a pity that you two gentlemen shot it down for being inelegant. And
>> ever since, we try to find a solution that is as simple, works as
>> reliably, also for testing, *and* appeases your tastes.
>
> I just would like to note that existence of file is used for both
> git-daemon and gitweb (the latter following the git-daemon example).
>
> So there is a precedent for the use of this mechanism.
I think you are thinking about git-daemon-export-ok (for 'git
daemon') and $GITWEB_EXPORT_OK file (for 'gitweb').
You do realize that it is apples-and-oranges [*1*] to take these as
analogous to what Dscho is trying to do, don't you?
First of all, these are to control access to each repository on the
server side; the presence of the file is checked in each repository.
What Dscho wants is to control the behaviour of an installation of
Git as a whole, no matter which repository is being accessed [*2*,
*3*].
More importantly, did you notice that git-daemon-export-ok predates
the configuration mechanism by a large margin? The "does the file
exist?" check done in a87e8be2ae ("Add a "git-daemon" that listens
on a TCP port", 2005-07-13) is a relic from the past [*4*], and
32f4aaccaa ("gitweb: export options", 2006-09-17) added
GITWEB_EXPORT_OK to mimic it, also long time ago [*5*]. They are
not something you would want to mimic in new programs these days.
Besides, $GIT_EXEC_PATH is where you place git subcommands. Who in
the right mind considers it even remotely sane to design a system
where you have to throw in a file that is not a command to /usr/bin
to control the behaviour of your system? [*6*]
So the "precedent" is irrelevant in the first place, and even if it
were relevant, it is a bad piece of advice to mimic it.
[Footnote]
*1* Or is it apples-and-pineapples these days?
*2* Not that I agree with that desire, if I understand him correctly
from his description against the approach based on an
environment variable. If a user has multiple installations and
not even aware of which one of them s/he is currently using, a
mechanism that affects only one of them (instead of consistently
affecting all of them) would lead to more confusion, I would
think.
*3* If such hermetically configured independent installations are
desirable, etc/gitconfig aka "git config --system" is a more
appropriate thing to use, and you do not need to do repository
discovery before you can read it.
*4* If we had config mechanism, we would have used it just like we
use daemon.* variables to control what services are enabled for
each repository.
*5* By that time, the config mechanism did already exist, so the
GITWEB_EXPORT_OK could have been a per-repository configuration,
but "gitweb" had another excuse to deviate from the norm. "Is
this repository visible?" was done during repository listing and
the script did not want to run "git config" in each and every
repository-like directory it encountered in File::Find::find().
*6* And I do not think $GIT_EXEC_PATH vs /usr/bin is
apples-and-oranges analogy.
^ permalink raw reply
* Re: [PATCHv2 4/4] submodule: add embed-git-dir function
From: Stefan Beller @ 2016-11-30 18:04 UTC (permalink / raw)
To: Duy Nguyen
Cc: Brandon Williams, Junio C Hamano, Git Mailing List,
Jonathan Nieder, Jens Lehmann, Heiko Voigt
In-Reply-To: <CACsJy8Ce3Oa-xJ4BwgRRy6neM=Jxkfqq7yboHZDXLDG2tu9GzQ@mail.gmail.com>
On Wed, Nov 30, 2016 at 3:14 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Wed, Nov 23, 2016 at 2:22 AM, Stefan Beller <sbeller@google.com> wrote:
>> +/*
>> + * Migrate the given submodule (and all its submodules recursively) from
>> + * having its git directory within the working tree to the git dir nested
>> + * in its superprojects git dir under modules/.
>> + */
>> +void migrate_submodule_gitdir(const char *prefix, const char *path,
>> + int recursive)
>
> Submodules and worktrees seem to have many things in common.
Yes. :)
> The first
> one is this. "git worktree move" on a worktree that contains
> submodules .git also benefits from something like this [1].
That patch is a sensible approach. :)
(By checking all files to not be submodules a worktree would not run
into problems like
a127331cd81, mv: allow moving nested submodules)
> I suggest
> you move this function to some neutral place and maybe rename it to
> relocate_gitdir() or something.
ok tell me where this neutral place is found?
(I'd prefer to not clobber it into cache.h *the* most neutral place in git)
Maybe dir.{c,h} ?
>
> It probably should take a bit flag instead of "recursive" here. One
> thing I would need is the ability to tell this function "I have moved
> all these .git dirs already (because I move whole worktree in one
> operation), here are the old and new locations of them, fix them up!".
> In other words, no rename() could be optionally skipped.
In the non-main working trees you'd also have a .git file linking
to the actual git dir and you'd only have to fix them up instead of moving.
>
> [1] https://public-inbox.org/git/20161128094319.16176-11-pclouds@gmail.com/T/#u
>
>> +{
>> + char *old_git_dir;
>> + const char *new_git_dir;
>> + const struct submodule *sub;
>> +
>> + old_git_dir = xstrfmt("%s/.git", path);
>> + if (read_gitfile(old_git_dir))
>> + /* If it is an actual gitfile, it doesn't need migration. */
>> + goto out;
>> +
>> + sub = submodule_from_path(null_sha1, path);
>> + if (!sub)
>> + die(_("Could not lookup name for submodule '%s'"),
>> + path);
>> +
>> + new_git_dir = git_common_path("modules/%s", sub->name);
>
> Why doesn't git_path() work here? This would make "modules" shared
> between worktrees, even though it's not normally. That inconsistency
> could cause trouble.
I thought that was a long term goal?
(I actually think about reviving the series you sent out a few weeks ago
to make worktree and submodules work well together)
So for that we'd want to have at least the object store shared across all
worktrees.
>
>> + if (safe_create_leading_directories_const(new_git_dir) < 0)
>> + die(_("could not create directory '%s'"), new_git_dir);
>> +
>> + if (!prefix)
>> + prefix = get_super_prefix();
>> + printf("Migrating git directory of %s%s from\n'%s' to\n'%s'\n",
>> + prefix ? prefix : "", path,
>> + real_path(old_git_dir), new_git_dir);
>> +
>> + if (rename(old_git_dir, new_git_dir) < 0)
>> + die_errno(_("Could not migrate git directory from '%s' to '%s'"),
>> + old_git_dir, new_git_dir);
>> +
>> + connect_work_tree_and_git_dir(path, new_git_dir);
>
> Another thing in common is, both submodules and worktrees use some
> form of textual symlinks. You need to fix up some here. But if this
> submodule has multiple worktreee, there may be some "symlinks" in
> .git/worktrees which would need fixing up as well.
We could signal that via one of the flag bits?
(e.g. FIXUP_WORKTREE_SYMLINKS )
>
> You don't have to do the fix up thing right away, but I think we
> should at least make sure we leave no dangling links behind (by
> die()ing early if we find a .git dir we can't handle yet)
> --
> Duy
^ permalink raw reply
* [PATCH v2 1/1] convert: git cherry-pick -Xrenormalize did not work
From: tboegi @ 2016-11-30 17:02 UTC (permalink / raw)
To: git, eevee.reply; +Cc: Torsten Bögershausen
In-Reply-To: <6a7e155-f399-c9f8-c69e-8164e0735dfb@veekun.com>
From: Torsten Bögershausen <tboegi@web.de>
Working with a repo that used to be all CRLF. At some point it
was changed to all LF, with `text=auto` in .gitattributes.
Trying to cherry-pick a commit from before the switchover fails:
$ git cherry-pick -Xrenormalize <commit>
fatal: CRLF would be replaced by LF in [path]
Commit 65237284 "unify the "auto" handling of CRLF" introduced
a regression:
Whenever crlf_action is CRLF_TEXT_XXX and not CRLF_AUTO_XXX,
SAFE_CRLF_RENORMALIZE was feed into check_safe_crlf().
This is wrong because here everything else than SAFE_CRLF_WARN is
treated as SAFE_CRLF_FAIL.
Call check_safe_crlf() only if checksafe is SAFE_CRLF_WARN or SAFE_CRLF_FAIL.
Reported-by: Eevee (Lexy Munroe) <eevee@veekun.com>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
---
convert.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/convert.c b/convert.c
index be91358..f8e4dfe 100644
--- a/convert.c
+++ b/convert.c
@@ -281,13 +281,13 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
+ - unless we want to renormalize in a merge or cherry-pick
*/
- if (checksafe == SAFE_CRLF_RENORMALIZE)
- checksafe = SAFE_CRLF_FALSE;
- else if (has_cr_in_index(path))
+ if ((checksafe != SAFE_CRLF_RENORMALIZE) && has_cr_in_index(path))
convert_crlf_into_lf = 0;
}
- if (checksafe && len) {
+ if ((checksafe == SAFE_CRLF_WARN ||
+ (checksafe == SAFE_CRLF_FAIL)) && len) {
struct text_stat new_stats;
memcpy(&new_stats, &stats, sizeof(new_stats));
/* simulate "git add" */
--
2.10.0
^ permalink raw reply related
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