2006-09-02 16:23:48 +00:00
|
|
|
/*
|
|
|
|
* GIT - The information manager from hell
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
|
|
|
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
|
|
|
|
* Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
|
|
|
|
* Copyright (C) 2006 Mike McCormack
|
|
|
|
* Copyright (C) 2006 Christian Couder
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
|
|
|
#include "quote.h"
|
|
|
|
|
2011-02-24 14:28:41 +00:00
|
|
|
/* Get a trace file descriptor from "key" env variable. */
|
|
|
|
static int get_trace_fd(const char *key, int *need_close)
|
2006-09-02 16:23:48 +00:00
|
|
|
{
|
2011-02-24 14:28:41 +00:00
|
|
|
char *trace = getenv(key);
|
2006-09-02 16:23:48 +00:00
|
|
|
|
2006-10-14 14:05:25 +00:00
|
|
|
if (!trace || !strcmp(trace, "") ||
|
|
|
|
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
2006-09-02 16:23:48 +00:00
|
|
|
return 0;
|
|
|
|
if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
|
|
|
|
return STDERR_FILENO;
|
|
|
|
if (strlen(trace) == 1 && isdigit(*trace))
|
|
|
|
return atoi(trace);
|
2007-11-25 22:29:03 +00:00
|
|
|
if (is_absolute_path(trace)) {
|
2006-09-02 16:23:48 +00:00
|
|
|
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
|
|
|
if (fd == -1) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Could not open '%s' for tracing: %s\n"
|
|
|
|
"Defaulting to tracing on stderr...\n",
|
|
|
|
trace, strerror(errno));
|
|
|
|
return STDERR_FILENO;
|
|
|
|
}
|
|
|
|
*need_close = 1;
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2011-02-24 14:28:41 +00:00
|
|
|
fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
|
2006-09-02 16:23:48 +00:00
|
|
|
fprintf(stderr, "If you want to trace into a file, "
|
2011-02-24 14:28:41 +00:00
|
|
|
"then please set %s to an absolute pathname "
|
|
|
|
"(starting with /).\n", key);
|
2006-09-02 16:23:48 +00:00
|
|
|
fprintf(stderr, "Defaulting to tracing on stderr...\n");
|
|
|
|
|
|
|
|
return STDERR_FILENO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char err_msg[] = "Could not trace into fd given by "
|
|
|
|
"GIT_TRACE environment variable";
|
|
|
|
|
2012-09-16 05:47:42 +00:00
|
|
|
static void trace_vprintf(const char *key, const char *fmt, va_list ap)
|
2006-09-02 16:23:48 +00:00
|
|
|
{
|
2011-02-26 05:08:53 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2006-09-02 16:23:48 +00:00
|
|
|
|
2011-02-24 14:29:50 +00:00
|
|
|
if (!trace_want(key))
|
2006-09-02 16:23:48 +00:00
|
|
|
return;
|
|
|
|
|
2010-12-21 17:24:18 +00:00
|
|
|
set_try_to_free_routine(NULL); /* is never reset */
|
2011-02-26 05:08:53 +00:00
|
|
|
strbuf_vaddf(&buf, fmt, ap);
|
2011-02-24 14:29:50 +00:00
|
|
|
trace_strbuf(key, &buf);
|
2007-09-19 22:42:13 +00:00
|
|
|
strbuf_release(&buf);
|
2006-09-02 16:23:48 +00:00
|
|
|
}
|
|
|
|
|
2013-07-10 00:18:40 +00:00
|
|
|
__attribute__((format (printf, 2, 3)))
|
shallow.c: the 8 steps to select new commits for .git/shallow
Suppose a fetch or push is requested between two shallow repositories
(with no history deepening or shortening). A pack that contains
necessary objects is transferred over together with .git/shallow of
the sender. The receiver has to determine whether it needs to update
.git/shallow if new refs needs new shallow comits.
The rule here is avoid updating .git/shallow by default. But we don't
want to waste the received pack. If the pack contains two refs, one
needs new shallow commits installed in .git/shallow and one does not,
we keep the latter and reject/warn about the former.
Even if .git/shallow update is allowed, we only add shallow commits
strictly necessary for the former ref (remember the sender can send
more shallow commits than necessary) and pay attention not to
accidentally cut the receiver history short (no history shortening is
asked for)
So the steps to figure out what ref need what new shallow commits are:
1. Split the sender shallow commit list into "ours" and "theirs" list
by has_sha1_file. Those that exist in current repo in "ours", the
remaining in "theirs".
2. Check the receiver .git/shallow, remove from "ours" the ones that
also exist in .git/shallow.
3. Fetch the new pack. Either install or unpack it.
4. Do has_sha1_file on "theirs" list again. Drop the ones that fail
has_sha1_file. Obviously the new pack does not need them.
5. If the pack is kept, remove from "ours" the ones that do not exist
in the new pack.
6. Walk the new refs to answer the question "what shallow commits,
both ours and theirs, are required in .git/shallow in order to add
this ref?". Shallow commits not associated to any refs are removed
from their respective list.
7. (*) Check reachability (from the current refs) of all remaining
commits in "ours". Those reachable are removed. We do not want to
cut any part of our (reachable) history. We only check up
commits. True reachability test is done by
check_everything_connected() at the end as usual.
8. Combine the final "ours" and "theirs" and add them all to
.git/shallow. Install new refs. The case where some hook rejects
some refs on a push is explained in more detail in the push
patches.
Of these steps, #6 and #7 are expensive. Both require walking through
some commits, or in the worst case all commits. And we rather avoid
them in at least common case, where the transferred pack does not
contain any shallow commits that the sender advertises. Let's look at
each scenario:
1) the sender has longer history than the receiver
All shallow commits from the sender will be put into "theirs" list
at step 1 because none of them exists in current repo. In the
common case, "theirs" becomes empty at step 4 and exit early.
2) the sender has shorter history than the receiver
All shallow commits from the sender are likely in "ours" list at
step 1. In the common case, if the new pack is kept, we could empty
"ours" and exit early at step 5.
If the pack is not kept, we hit the expensive step 6 then exit
after "ours" is emptied. There'll be only a handful of objects to
walk in fast-forward case. If it's forced update, we may need to
walk to the bottom.
3) the sender has same .git/shallow as the receiver
This is similar to case 2 except that "ours" should be emptied at
step 2 and exit early.
A fetch after "clone --depth=X" is case 1. A fetch after "clone" (from
a shallow repo) is case 3. Luckily they're cheap for the common case.
A push from "clone --depth=X" falls into case 2, which is expensive.
Some more work may be done at the sender/client side to avoid more
work on the server side: if the transferred pack does not contain any
shallow commits, send-pack should not send any shallow commits to the
receive-pack, effectively turning it into a normal push and avoid all
steps.
This patch implements all steps except #3, already handled by
fetch-pack and receive-pack, #6 and #7, which has their own patch due
to their size.
(*) in previous versions step 7 was put before step 3. I reorder it so
that the common case that keeps the pack does not need to walk
commits at all. In future if we implement faster commit
reachability check (maybe with the help of pack bitmaps or commit
cache), step 7 could become cheap and be moved up before 6 again.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-12-05 13:02:35 +00:00
|
|
|
void trace_printf_key(const char *key, const char *fmt, ...)
|
2011-02-24 14:28:41 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
trace_vprintf(key, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2011-02-24 14:28:15 +00:00
|
|
|
void trace_printf(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2011-02-24 14:28:41 +00:00
|
|
|
trace_vprintf("GIT_TRACE", fmt, ap);
|
2011-02-24 14:28:15 +00:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2011-02-24 14:29:50 +00:00
|
|
|
void trace_strbuf(const char *key, const struct strbuf *buf)
|
|
|
|
{
|
|
|
|
int fd, need_close = 0;
|
|
|
|
|
|
|
|
fd = get_trace_fd(key, &need_close);
|
|
|
|
if (!fd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
|
|
|
|
|
|
|
|
if (need_close)
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2007-12-03 04:51:50 +00:00
|
|
|
void trace_argv_printf(const char **argv, const char *fmt, ...)
|
2006-09-02 16:23:48 +00:00
|
|
|
{
|
2011-02-26 05:08:53 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
nfv?asprintf are broken without va_copy, workaround them.
* drop nfasprintf.
* move nfvasprintf into imap-send.c back, and let it work on a 8k buffer,
and die() in case of overflow. It should be enough for imap commands, if
someone cares about imap-send, he's welcomed to fix it properly.
* replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf
logic, it's one place, we'll live with it.
To ease the change, output_buffer string list is replaced with a strbuf ;)
* rework trace.c to call vsnprintf itself. It's used to format strerror()s
and git command names, it should never be more than a few octets long, let
it work on a 8k static buffer with vsnprintf or die loudly.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
2007-09-20 08:43:11 +00:00
|
|
|
va_list ap;
|
2011-02-26 05:08:53 +00:00
|
|
|
int fd, need_close = 0;
|
2006-09-02 16:23:48 +00:00
|
|
|
|
2011-02-24 14:28:41 +00:00
|
|
|
fd = get_trace_fd("GIT_TRACE", &need_close);
|
2006-09-02 16:23:48 +00:00
|
|
|
if (!fd)
|
|
|
|
return;
|
|
|
|
|
2010-12-21 17:24:18 +00:00
|
|
|
set_try_to_free_routine(NULL); /* is never reset */
|
nfv?asprintf are broken without va_copy, workaround them.
* drop nfasprintf.
* move nfvasprintf into imap-send.c back, and let it work on a 8k buffer,
and die() in case of overflow. It should be enough for imap commands, if
someone cares about imap-send, he's welcomed to fix it properly.
* replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf
logic, it's one place, we'll live with it.
To ease the change, output_buffer string list is replaced with a strbuf ;)
* rework trace.c to call vsnprintf itself. It's used to format strerror()s
and git command names, it should never be more than a few octets long, let
it work on a 8k static buffer with vsnprintf or die loudly.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
2007-09-20 08:43:11 +00:00
|
|
|
va_start(ap, fmt);
|
2011-02-26 05:08:53 +00:00
|
|
|
strbuf_vaddf(&buf, fmt, ap);
|
nfv?asprintf are broken without va_copy, workaround them.
* drop nfasprintf.
* move nfvasprintf into imap-send.c back, and let it work on a 8k buffer,
and die() in case of overflow. It should be enough for imap commands, if
someone cares about imap-send, he's welcomed to fix it properly.
* replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf
logic, it's one place, we'll live with it.
To ease the change, output_buffer string list is replaced with a strbuf ;)
* rework trace.c to call vsnprintf itself. It's used to format strerror()s
and git command names, it should never be more than a few octets long, let
it work on a 8k static buffer with vsnprintf or die loudly.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
2007-09-20 08:43:11 +00:00
|
|
|
va_end(ap);
|
2006-09-02 16:23:48 +00:00
|
|
|
|
2007-12-03 04:51:50 +00:00
|
|
|
sq_quote_argv(&buf, argv, 0);
|
2007-09-19 22:42:13 +00:00
|
|
|
strbuf_addch(&buf, '\n');
|
|
|
|
write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
|
|
|
|
strbuf_release(&buf);
|
2006-09-02 16:23:48 +00:00
|
|
|
|
|
|
|
if (need_close)
|
|
|
|
close(fd);
|
|
|
|
}
|
2010-11-26 15:31:57 +00:00
|
|
|
|
|
|
|
static const char *quote_crnl(const char *path)
|
|
|
|
{
|
|
|
|
static char new_path[PATH_MAX];
|
|
|
|
const char *p2 = path;
|
|
|
|
char *p1 = new_path;
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
while (*p2) {
|
|
|
|
switch (*p2) {
|
|
|
|
case '\\': *p1++ = '\\'; *p1++ = '\\'; break;
|
|
|
|
case '\n': *p1++ = '\\'; *p1++ = 'n'; break;
|
|
|
|
case '\r': *p1++ = '\\'; *p1++ = 'r'; break;
|
|
|
|
default:
|
|
|
|
*p1++ = *p2;
|
|
|
|
}
|
|
|
|
p2++;
|
|
|
|
}
|
|
|
|
*p1 = '\0';
|
|
|
|
return new_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: move prefix to startup_info struct and get rid of this arg */
|
|
|
|
void trace_repo_setup(const char *prefix)
|
|
|
|
{
|
2011-02-24 14:30:30 +00:00
|
|
|
static const char *key = "GIT_TRACE_SETUP";
|
2011-01-06 00:30:01 +00:00
|
|
|
const char *git_work_tree;
|
2010-11-26 15:31:57 +00:00
|
|
|
char cwd[PATH_MAX];
|
|
|
|
|
2011-02-24 14:30:30 +00:00
|
|
|
if (!trace_want(key))
|
2010-11-26 15:31:57 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!getcwd(cwd, PATH_MAX))
|
|
|
|
die("Unable to get current working directory");
|
|
|
|
|
2011-01-06 00:30:01 +00:00
|
|
|
if (!(git_work_tree = get_git_work_tree()))
|
|
|
|
git_work_tree = "(null)";
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "(null)";
|
|
|
|
|
2011-02-24 14:30:30 +00:00
|
|
|
trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
|
|
|
|
trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
|
|
|
|
trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd));
|
|
|
|
trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
|
2010-11-26 15:31:57 +00:00
|
|
|
}
|
2011-02-24 14:28:59 +00:00
|
|
|
|
|
|
|
int trace_want(const char *key)
|
|
|
|
{
|
|
|
|
const char *trace = getenv(key);
|
|
|
|
|
|
|
|
if (!trace || !strcmp(trace, "") ||
|
|
|
|
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|