* [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-02 19:51 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
` (26 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
We've been avoiding PATH_MAX whenever possible. This patch makes
get_pathname() return a strbuf and updates the callers to take
advantage of this. The code is simplified as we no longer need to
worry about buffer overflow.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 120 ++++++++++++++++++++++++++++-------------------------------------
1 file changed, 51 insertions(+), 69 deletions(-)
diff --git a/path.c b/path.c
index 24594c4..5346700 100644
--- a/path.c
+++ b/path.c
@@ -16,11 +16,15 @@ static int get_st_mode_bits(const char *path, int *mode)
static char bad_path[] = "/bad-path/";
-static char *get_pathname(void)
+static struct strbuf *get_pathname(void)
{
- static char pathname_array[4][PATH_MAX];
+ static struct strbuf pathname_array[4] = {
+ STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+ };
static int index;
- return pathname_array[3 & ++index];
+ struct strbuf *sb = &pathname_array[3 & ++index];
+ strbuf_reset(sb);
+ return sb;
}
static char *cleanup_path(char *path)
@@ -34,6 +38,13 @@ static char *cleanup_path(char *path)
return path;
}
+static void strbuf_cleanup_path(struct strbuf *sb)
+{
+ char *path = cleanup_path(sb->buf);
+ if (path > sb->buf)
+ strbuf_remove(sb, 0, path - sb->buf);
+}
+
char *mksnpath(char *buf, size_t n, const char *fmt, ...)
{
va_list args;
@@ -49,85 +60,70 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
-static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
{
const char *git_dir = get_git_dir();
- size_t len;
-
- len = strlen(git_dir);
- if (n < len + 1)
- goto bad;
- memcpy(buf, git_dir, len);
- if (len && !is_dir_sep(git_dir[len-1]))
- buf[len++] = '/';
- len += vsnprintf(buf + len, n - len, fmt, args);
- if (len >= n)
- goto bad;
- return cleanup_path(buf);
-bad:
- strlcpy(buf, bad_path, n);
- return buf;
+ strbuf_addstr(buf, git_dir);
+ if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+ strbuf_addch(buf, '/');
+ strbuf_vaddf(buf, fmt, args);
+ strbuf_cleanup_path(buf);
}
char *git_snpath(char *buf, size_t n, const char *fmt, ...)
{
- char *ret;
+ struct strbuf sb = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- ret = vsnpath(buf, n, fmt, args);
+ vsnpath(&sb, fmt, args);
va_end(args);
- return ret;
+ if (sb.len >= n)
+ strlcpy(buf, bad_path, n);
+ else
+ memcpy(buf, sb.buf, sb.len + 1);
+ strbuf_release(&sb);
+ return buf;
}
char *git_pathdup(const char *fmt, ...)
{
- char path[PATH_MAX], *ret;
+ struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- ret = vsnpath(path, sizeof(path), fmt, args);
+ vsnpath(&path, fmt, args);
va_end(args);
- return xstrdup(ret);
+ return strbuf_detach(&path, NULL);
}
char *mkpathdup(const char *fmt, ...)
{
- char *path;
struct strbuf sb = STRBUF_INIT;
va_list args;
-
va_start(args, fmt);
strbuf_vaddf(&sb, fmt, args);
va_end(args);
- path = xstrdup(cleanup_path(sb.buf));
-
- strbuf_release(&sb);
- return path;
+ strbuf_cleanup_path(&sb);
+ return strbuf_detach(&sb, NULL);
}
char *mkpath(const char *fmt, ...)
{
va_list args;
- unsigned len;
- char *pathname = get_pathname();
-
+ struct strbuf *pathname = get_pathname();
va_start(args, fmt);
- len = vsnprintf(pathname, PATH_MAX, fmt, args);
+ strbuf_vaddf(pathname, fmt, args);
va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
+ return cleanup_path(pathname->buf);
}
char *git_path(const char *fmt, ...)
{
- char *pathname = get_pathname();
+ struct strbuf *pathname = get_pathname();
va_list args;
- char *ret;
-
va_start(args, fmt);
- ret = vsnpath(pathname, PATH_MAX, fmt, args);
+ vsnpath(pathname, fmt, args);
va_end(args);
- return ret;
+ return pathname->buf;
}
void home_config_paths(char **global, char **xdg, char *file)
@@ -158,41 +154,27 @@ void home_config_paths(char **global, char **xdg, char *file)
char *git_path_submodule(const char *path, const char *fmt, ...)
{
- char *pathname = get_pathname();
- struct strbuf buf = STRBUF_INIT;
+ struct strbuf *buf = get_pathname();
const char *git_dir;
va_list args;
- unsigned len;
-
- len = strlen(path);
- if (len > PATH_MAX-100)
- return bad_path;
- strbuf_addstr(&buf, path);
- if (len && path[len-1] != '/')
- strbuf_addch(&buf, '/');
- strbuf_addstr(&buf, ".git");
+ strbuf_addstr(buf, path);
+ if (buf->len && buf->buf[buf->len - 1] != '/')
+ strbuf_addch(buf, '/');
+ strbuf_addstr(buf, ".git");
- git_dir = read_gitfile(buf.buf);
+ git_dir = read_gitfile(buf->buf);
if (git_dir) {
- strbuf_reset(&buf);
- strbuf_addstr(&buf, git_dir);
+ strbuf_reset(buf);
+ strbuf_addstr(buf, git_dir);
}
- strbuf_addch(&buf, '/');
-
- if (buf.len >= PATH_MAX)
- return bad_path;
- memcpy(pathname, buf.buf, buf.len + 1);
-
- strbuf_release(&buf);
- len = strlen(pathname);
+ strbuf_addch(buf, '/');
va_start(args, fmt);
- len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+ strbuf_vaddf(buf, fmt, args);
va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
+ strbuf_cleanup_path(buf);
+ return buf->buf;
}
int validate_headref(const char *path)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-01 12:12 ` [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
@ 2014-03-02 19:51 ` Eric Sunshine
2014-03-03 0:14 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Eric Sunshine @ 2014-03-02 19:51 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> We've been avoiding PATH_MAX whenever possible. This patch makes
> get_pathname() return a strbuf and updates the callers to take
> advantage of this. The code is simplified as we no longer need to
> worry about buffer overflow.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/path.c b/path.c
> index 24594c4..5346700 100644
> --- a/path.c
> +++ b/path.c
> @@ -49,85 +60,70 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
> return cleanup_path(buf);
> }
>
> -static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
> +static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
> {
> const char *git_dir = get_git_dir();
> - size_t len;
> -
> - len = strlen(git_dir);
> - if (n < len + 1)
> - goto bad;
> - memcpy(buf, git_dir, len);
> - if (len && !is_dir_sep(git_dir[len-1]))
> - buf[len++] = '/';
> - len += vsnprintf(buf + len, n - len, fmt, args);
> - if (len >= n)
> - goto bad;
> - return cleanup_path(buf);
> -bad:
> - strlcpy(buf, bad_path, n);
> - return buf;
> + strbuf_addstr(buf, git_dir);
> + if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
> + strbuf_addch(buf, '/');
> + strbuf_vaddf(buf, fmt, args);
> + strbuf_cleanup_path(buf);
> }
There's a slight semantic change here. The old code overwrote 'buf',
but the new code appends to 'buf'. For someone familiar with
sprintf(), or typical va_list or variadic functions there may be an
intuitive expectation that 'buf' will be overwritten. Should this code
be doing strbuf_reset() as its first action (or should that be the
responsibility of the caller who is reusing 'buff')?
> char *mkpath(const char *fmt, ...)
> {
> va_list args;
> - unsigned len;
> - char *pathname = get_pathname();
> -
> + struct strbuf *pathname = get_pathname();
> va_start(args, fmt);
> - len = vsnprintf(pathname, PATH_MAX, fmt, args);
> + strbuf_vaddf(pathname, fmt, args);
> va_end(args);
> - if (len >= PATH_MAX)
> - return bad_path;
> - return cleanup_path(pathname);
> + return cleanup_path(pathname->buf);
> }
Prior to this change, it was possible (though probably not
recommended) for a caller to append gunk to the returned path up to
PATH_MAX without worrying about stomping memory. With the change, this
is no longer true. Should the function be changed to return 'const
char *' to enforce this restriction?
> char *git_path(const char *fmt, ...)
> {
> - char *pathname = get_pathname();
> + struct strbuf *pathname = get_pathname();
> va_list args;
> - char *ret;
> -
> va_start(args, fmt);
> - ret = vsnpath(pathname, PATH_MAX, fmt, args);
> + vsnpath(pathname, fmt, args);
> va_end(args);
> - return ret;
> + return pathname->buf;
> }
Ditto.
>
> void home_config_paths(char **global, char **xdg, char *file)
> @@ -158,41 +154,27 @@ void home_config_paths(char **global, char **xdg, char *file)
>
> char *git_path_submodule(const char *path, const char *fmt, ...)
> {
> - char *pathname = get_pathname();
> - struct strbuf buf = STRBUF_INIT;
> + struct strbuf *buf = get_pathname();
> ...
> + strbuf_cleanup_path(buf);
> + return buf->buf;
> }
And here?
>
> int validate_headref(const char *path)
> --
> 1.9.0.40.gaa8c3ea
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-02 19:51 ` Eric Sunshine
@ 2014-03-03 0:14 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-03 0:14 UTC (permalink / raw)
To: Eric Sunshine; +Cc: Git List, Junio C Hamano
On Mon, Mar 3, 2014 at 2:51 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> We've been avoiding PATH_MAX whenever possible. This patch makes
>> get_pathname() return a strbuf and updates the callers to take
>> advantage of this. The code is simplified as we no longer need to
>> worry about buffer overflow.
>>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> diff --git a/path.c b/path.c
>> index 24594c4..5346700 100644
>> --- a/path.c
>> +++ b/path.c
>> @@ -49,85 +60,70 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
>> return cleanup_path(buf);
>> }
>>
>> -static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
>> +static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
>> {
>> const char *git_dir = get_git_dir();
>> - size_t len;
>> -
>> - len = strlen(git_dir);
>> - if (n < len + 1)
>> - goto bad;
>> - memcpy(buf, git_dir, len);
>> - if (len && !is_dir_sep(git_dir[len-1]))
>> - buf[len++] = '/';
>> - len += vsnprintf(buf + len, n - len, fmt, args);
>> - if (len >= n)
>> - goto bad;
>> - return cleanup_path(buf);
>> -bad:
>> - strlcpy(buf, bad_path, n);
>> - return buf;
>> + strbuf_addstr(buf, git_dir);
>> + if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
>> + strbuf_addch(buf, '/');
>> + strbuf_vaddf(buf, fmt, args);
>> + strbuf_cleanup_path(buf);
>> }
>
> There's a slight semantic change here. The old code overwrote 'buf',
> but the new code appends to 'buf'. For someone familiar with
> sprintf(), or typical va_list or variadic functions there may be an
> intuitive expectation that 'buf' will be overwritten. Should this code
> be doing strbuf_reset() as its first action (or should that be the
> responsibility of the caller who is reusing 'buff')?
those callers that use get_pathname() already have strbuf reset, so
I'd say let the remaining callers reset the buffer.
>
>> char *mkpath(const char *fmt, ...)
>> {
>> va_list args;
>> - unsigned len;
>> - char *pathname = get_pathname();
>> -
>> + struct strbuf *pathname = get_pathname();
>> va_start(args, fmt);
>> - len = vsnprintf(pathname, PATH_MAX, fmt, args);
>> + strbuf_vaddf(pathname, fmt, args);
>> va_end(args);
>> - if (len >= PATH_MAX)
>> - return bad_path;
>> - return cleanup_path(pathname);
>> + return cleanup_path(pathname->buf);
>> }
>
> Prior to this change, it was possible (though probably not
> recommended) for a caller to append gunk to the returned path up to
> PATH_MAX without worrying about stomping memory. With the change, this
> is no longer true. Should the function be changed to return 'const
> char *' to enforce this restriction?
Sure. Will do (same for below)
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path()
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-03 0:02 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 03/27] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
` (25 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In the previous patch, git_snpath() is modified to allocate a new
strbuf buffer because vsnpath() needs that. But that makes it awkward
because git_snpath() receives a pre-allocated buffer from outside and
has to copy data back. Rename it to strbuf_git_path() and make it
receive strbuf directly.
The conversion from git_snpath() to git_path() in
update_refs_for_switch() is safe because that function does not keep
any pointer to the round-robin buffer pool allocated by
get_pathname().
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 22 +++++++++---------
cache.h | 4 ++--
path.c | 11 ++-------
refs.c | 66 +++++++++++++++++++++++++++++++++++-------------------
refs.h | 2 +-
5 files changed, 58 insertions(+), 47 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5df3837..0570e41 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -585,18 +585,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (opts->new_orphan_branch) {
if (opts->new_branch_log && !log_all_ref_updates) {
int temp;
- char log_file[PATH_MAX];
+ struct strbuf log_file = STRBUF_INIT;
char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+ int ret;
temp = log_all_ref_updates;
log_all_ref_updates = 1;
- if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+ ret = log_ref_setup(ref_name, &log_file);
+ log_all_ref_updates = temp;
+ strbuf_release(&log_file);
+ if (ret) {
fprintf(stderr, _("Can not do reflog for '%s'\n"),
opts->new_orphan_branch);
- log_all_ref_updates = temp;
return;
}
- log_all_ref_updates = temp;
}
}
else
@@ -651,14 +653,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
new->name);
}
}
- if (old->path && old->name) {
- char log_file[PATH_MAX], ref_file[PATH_MAX];
-
- git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
- git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
- if (!file_exists(ref_file) && file_exists(log_file))
- remove_path(log_file);
- }
+ if (old->path && old->name &&
+ !file_exists(git_path("%s", old->path)) &&
+ file_exists(git_path("logs/%s", old->path)))
+ remove_path(git_path("logs/%s", old->path));
}
remove_branch_state();
strbuf_release(&msg);
diff --git a/cache.h b/cache.h
index dc040fb..8d3697e 100644
--- a/cache.h
+++ b/cache.h
@@ -646,8 +646,8 @@ extern int check_repository_format(void);
extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
-extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
- __attribute__((format (printf, 3, 4)));
+extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
extern char *git_pathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
extern char *mkpathdup(const char *fmt, ...)
diff --git a/path.c b/path.c
index 5346700..b52a16f 100644
--- a/path.c
+++ b/path.c
@@ -70,19 +70,12 @@ static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
strbuf_cleanup_path(buf);
}
-char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
- struct strbuf sb = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- vsnpath(&sb, fmt, args);
+ vsnpath(sb, fmt, args);
va_end(args);
- if (sb.len >= n)
- strlcpy(buf, bad_path, n);
- else
- memcpy(buf, sb.buf, sb.len + 1);
- strbuf_release(&sb);
- return buf;
}
char *git_pathdup(const char *fmt, ...)
diff --git a/refs.c b/refs.c
index 89228e2..434bd5e 100644
--- a/refs.c
+++ b/refs.c
@@ -1325,10 +1325,12 @@ static const char *handle_missing_loose_ref(const char *refname,
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
{
+ struct strbuf sb_path = STRBUF_INIT;
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char refname_buffer[256];
+ const char *ret;
if (flag)
*flag = 0;
@@ -1337,15 +1339,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
return NULL;
for (;;) {
- char path[PATH_MAX];
+ const char *path;
struct stat st;
char *buf;
int fd;
if (--depth < 0)
- return NULL;
+ goto fail;
- git_snpath(path, sizeof(path), "%s", refname);
+ strbuf_reset(&sb_path);
+ strbuf_git_path(&sb_path, "%s", refname);
+ path = sb_path.buf;
/*
* We might have to loop back here to avoid a race
@@ -1359,10 +1363,11 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
stat_ref:
if (lstat(path, &st) < 0) {
if (errno == ENOENT)
- return handle_missing_loose_ref(refname, sha1,
- reading, flag);
+ ret = handle_missing_loose_ref(refname, sha1,
+ reading, flag);
else
- return NULL;
+ ret = NULL;
+ goto done;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
@@ -1373,7 +1378,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* inconsistent with lstat; retry */
goto stat_ref;
else
- return NULL;
+ goto fail;
}
buffer[len] = 0;
if (starts_with(buffer, "refs/") &&
@@ -1389,7 +1394,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* Is it a directory? */
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
- return NULL;
+ goto fail;
}
/*
@@ -1402,12 +1407,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* inconsistent with lstat; retry */
goto stat_ref;
else
- return NULL;
+ goto fail;
}
+
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0)
- return NULL;
+ goto fail;
while (len && isspace(buffer[len-1]))
len--;
buffer[len] = '\0';
@@ -1424,9 +1430,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
(buffer[40] != '\0' && !isspace(buffer[40]))) {
if (flag)
*flag |= REF_ISBROKEN;
- return NULL;
+ goto fail;
}
- return refname;
+ ret = refname;
+ goto done;
}
if (flag)
*flag |= REF_ISSYMREF;
@@ -1436,10 +1443,15 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flag)
*flag |= REF_ISBROKEN;
- return NULL;
+ goto fail;
}
refname = strcpy(refname_buffer, buf);
}
+fail:
+ ret = NULL;
+done:
+ strbuf_release(&sb_path);
+ return ret;
}
char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -2717,17 +2729,19 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
-int log_ref_setup(const char *refname, char *logfile, int bufsize)
+int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
{
int logfd, oflags = O_APPEND | O_WRONLY;
+ const char *logfile;
- git_snpath(logfile, bufsize, "logs/%s", refname);
+ strbuf_git_path(sb_logfile, "logs/%s", refname);
+ logfile = sb_logfile->buf;
if (log_all_ref_updates &&
(starts_with(refname, "refs/heads/") ||
starts_with(refname, "refs/remotes/") ||
starts_with(refname, "refs/notes/") ||
!strcmp(refname, "HEAD"))) {
- if (safe_create_leading_directories(logfile) < 0)
+ if (safe_create_leading_directories(sb_logfile->buf) < 0)
return error("unable to create directory for %s",
logfile);
oflags |= O_CREAT;
@@ -2762,20 +2776,22 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
int logfd, result, written, oflags = O_APPEND | O_WRONLY;
unsigned maxlen, len;
int msglen;
- char log_file[PATH_MAX];
+ struct strbuf sb_log_file = STRBUF_INIT;
+ const char *log_file;
char *logrec;
const char *committer;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, log_file, sizeof(log_file));
+ result = log_ref_setup(refname, &sb_log_file);
if (result)
- return result;
+ goto done;
+ log_file = sb_log_file.buf;
logfd = open(log_file, oflags);
if (logfd < 0)
- return 0;
+ goto done;
msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(0);
maxlen = strlen(committer) + msglen + 100;
@@ -2788,9 +2804,13 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
- if (close(logfd) != 0 || written != len)
- return error("Unable to append to %s", log_file);
- return 0;
+ if (close(logfd) != 0 || written != len) {
+ error("Unable to append to %s", log_file);
+ result = -1;
+ }
+done:
+ strbuf_release(&sb_log_file);
+ return result;
}
static int is_branch(const char *refname)
diff --git a/refs.h b/refs.h
index 87a1a79..783033a 100644
--- a/refs.h
+++ b/refs.h
@@ -166,7 +166,7 @@ extern void unlock_ref(struct ref_lock *lock);
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
/** Setup reflog before using. **/
-int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+int log_ref_setup(const char *ref_name, struct strbuf *logfile);
/** Reads log for the value of ref during at_time. **/
extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path()
2014-03-01 12:12 ` [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-03 0:02 ` Eric Sunshine
2014-03-03 0:15 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Eric Sunshine @ 2014-03-03 0:02 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> In the previous patch, git_snpath() is modified to allocate a new
> strbuf buffer because vsnpath() needs that. But that makes it awkward
> because git_snpath() receives a pre-allocated buffer from outside and
> has to copy data back. Rename it to strbuf_git_path() and make it
> receive strbuf directly.
>
> The conversion from git_snpath() to git_path() in
> update_refs_for_switch() is safe because that function does not keep
> any pointer to the round-robin buffer pool allocated by
> get_pathname().
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/refs.c b/refs.c
> index 89228e2..434bd5e 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -2717,17 +2729,19 @@ static int copy_msg(char *buf, const char *msg)
> return cp - buf;
> }
>
> -int log_ref_setup(const char *refname, char *logfile, int bufsize)
> +int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
> {
> int logfd, oflags = O_APPEND | O_WRONLY;
> + const char *logfile;
>
> - git_snpath(logfile, bufsize, "logs/%s", refname);
> + strbuf_git_path(sb_logfile, "logs/%s", refname);
> + logfile = sb_logfile->buf;
> if (log_all_ref_updates &&
> (starts_with(refname, "refs/heads/") ||
> starts_with(refname, "refs/remotes/") ||
> starts_with(refname, "refs/notes/") ||
> !strcmp(refname, "HEAD"))) {
> - if (safe_create_leading_directories(logfile) < 0)
> + if (safe_create_leading_directories(sb_logfile->buf) < 0)
At this point, 'logfile' is still 'sb_logfile->buf', so do you really
need this change?
> return error("unable to create directory for %s",
> logfile);
> oflags |= O_CREAT;
> @@ -2762,20 +2776,22 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
> int logfd, result, written, oflags = O_APPEND | O_WRONLY;
> unsigned maxlen, len;
> int msglen;
> - char log_file[PATH_MAX];
> + struct strbuf sb_log_file = STRBUF_INIT;
> + const char *log_file;
> char *logrec;
> const char *committer;
>
> if (log_all_ref_updates < 0)
> log_all_ref_updates = !is_bare_repository();
>
> - result = log_ref_setup(refname, log_file, sizeof(log_file));
> + result = log_ref_setup(refname, &sb_log_file);
> if (result)
> - return result;
> + goto done;
> + log_file = sb_log_file.buf;
>
> logfd = open(log_file, oflags);
> if (logfd < 0)
> - return 0;
> + goto done;
> msglen = msg ? strlen(msg) : 0;
> committer = git_committer_info(0);
> maxlen = strlen(committer) + msglen + 100;
> --
> 1.9.0.40.gaa8c3ea
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path()
2014-03-03 0:02 ` Eric Sunshine
@ 2014-03-03 0:15 ` Duy Nguyen
2014-03-07 5:03 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Duy Nguyen @ 2014-03-03 0:15 UTC (permalink / raw)
To: Eric Sunshine; +Cc: Git List, Junio C Hamano
On Mon, Mar 3, 2014 at 7:02 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> In the previous patch, git_snpath() is modified to allocate a new
>> strbuf buffer because vsnpath() needs that. But that makes it awkward
>> because git_snpath() receives a pre-allocated buffer from outside and
>> has to copy data back. Rename it to strbuf_git_path() and make it
>> receive strbuf directly.
>>
>> The conversion from git_snpath() to git_path() in
>> update_refs_for_switch() is safe because that function does not keep
>> any pointer to the round-robin buffer pool allocated by
>> get_pathname().
>>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> diff --git a/refs.c b/refs.c
>> index 89228e2..434bd5e 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -2717,17 +2729,19 @@ static int copy_msg(char *buf, const char *msg)
>> return cp - buf;
>> }
>>
>> -int log_ref_setup(const char *refname, char *logfile, int bufsize)
>> +int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
>> {
>> int logfd, oflags = O_APPEND | O_WRONLY;
>> + const char *logfile;
>>
>> - git_snpath(logfile, bufsize, "logs/%s", refname);
>> + strbuf_git_path(sb_logfile, "logs/%s", refname);
>> + logfile = sb_logfile->buf;
>> if (log_all_ref_updates &&
>> (starts_with(refname, "refs/heads/") ||
>> starts_with(refname, "refs/remotes/") ||
>> starts_with(refname, "refs/notes/") ||
>> !strcmp(refname, "HEAD"))) {
>> - if (safe_create_leading_directories(logfile) < 0)
>> + if (safe_create_leading_directories(sb_logfile->buf) < 0)
>
> At this point, 'logfile' is still 'sb_logfile->buf', so do you really
> need this change?
Junio made the same comment last time and I missed it. Will update.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path()
2014-03-03 0:15 ` Duy Nguyen
@ 2014-03-07 5:03 ` Duy Nguyen
2014-03-07 5:26 ` Eric Sunshine
0 siblings, 1 reply; 169+ messages in thread
From: Duy Nguyen @ 2014-03-07 5:03 UTC (permalink / raw)
To: Eric Sunshine; +Cc: Git List, Junio C Hamano
On Mon, Mar 3, 2014 at 7:15 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Mon, Mar 3, 2014 at 7:02 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>> In the previous patch, git_snpath() is modified to allocate a new
>>> strbuf buffer because vsnpath() needs that. But that makes it awkward
>>> because git_snpath() receives a pre-allocated buffer from outside and
>>> has to copy data back. Rename it to strbuf_git_path() and make it
>>> receive strbuf directly.
>>>
>>> The conversion from git_snpath() to git_path() in
>>> update_refs_for_switch() is safe because that function does not keep
>>> any pointer to the round-robin buffer pool allocated by
>>> get_pathname().
>>>
>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>> ---
>>> diff --git a/refs.c b/refs.c
>>> index 89228e2..434bd5e 100644
>>> --- a/refs.c
>>> +++ b/refs.c
>>> @@ -2717,17 +2729,19 @@ static int copy_msg(char *buf, const char *msg)
>>> return cp - buf;
>>> }
>>>
>>> -int log_ref_setup(const char *refname, char *logfile, int bufsize)
>>> +int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
>>> {
>>> int logfd, oflags = O_APPEND | O_WRONLY;
>>> + const char *logfile;
>>>
>>> - git_snpath(logfile, bufsize, "logs/%s", refname);
>>> + strbuf_git_path(sb_logfile, "logs/%s", refname);
>>> + logfile = sb_logfile->buf;
>>> if (log_all_ref_updates &&
>>> (starts_with(refname, "refs/heads/") ||
>>> starts_with(refname, "refs/remotes/") ||
>>> starts_with(refname, "refs/notes/") ||
>>> !strcmp(refname, "HEAD"))) {
>>> - if (safe_create_leading_directories(logfile) < 0)
>>> + if (safe_create_leading_directories(sb_logfile->buf) < 0)
>>
>> At this point, 'logfile' is still 'sb_logfile->buf', so do you really
>> need this change?
>
> Junio made the same comment last time and I missed it. Will update.
No I will not :-) safe_create_leading_directories takes an editable
string, but logfile is now a const string. We could use
s_c_l_d_const() but that one will make a copy of the string
unncessarily. Will make a note in the commit message though.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path()
2014-03-07 5:03 ` Duy Nguyen
@ 2014-03-07 5:26 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-07 5:26 UTC (permalink / raw)
To: Duy Nguyen; +Cc: Git List, Junio C Hamano
On Fri, Mar 7, 2014 at 12:03 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Mon, Mar 3, 2014 at 7:15 AM, Duy Nguyen <pclouds@gmail.com> wrote:
>> On Mon, Mar 3, 2014 at 7:02 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>>> On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>>> In the previous patch, git_snpath() is modified to allocate a new
>>>> strbuf buffer because vsnpath() needs that. But that makes it awkward
>>>> because git_snpath() receives a pre-allocated buffer from outside and
>>>> has to copy data back. Rename it to strbuf_git_path() and make it
>>>> receive strbuf directly.
>>>>
>>>> The conversion from git_snpath() to git_path() in
>>>> update_refs_for_switch() is safe because that function does not keep
>>>> any pointer to the round-robin buffer pool allocated by
>>>> get_pathname().
>>>>
>>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>>> ---
>>>> diff --git a/refs.c b/refs.c
>>>> index 89228e2..434bd5e 100644
>>>> --- a/refs.c
>>>> +++ b/refs.c
>>>> @@ -2717,17 +2729,19 @@ static int copy_msg(char *buf, const char *msg)
>>>> return cp - buf;
>>>> }
>>>>
>>>> -int log_ref_setup(const char *refname, char *logfile, int bufsize)
>>>> +int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
>>>> {
>>>> int logfd, oflags = O_APPEND | O_WRONLY;
>>>> + const char *logfile;
>>>>
>>>> - git_snpath(logfile, bufsize, "logs/%s", refname);
>>>> + strbuf_git_path(sb_logfile, "logs/%s", refname);
>>>> + logfile = sb_logfile->buf;
>>>> if (log_all_ref_updates &&
>>>> (starts_with(refname, "refs/heads/") ||
>>>> starts_with(refname, "refs/remotes/") ||
>>>> starts_with(refname, "refs/notes/") ||
>>>> !strcmp(refname, "HEAD"))) {
>>>> - if (safe_create_leading_directories(logfile) < 0)
>>>> + if (safe_create_leading_directories(sb_logfile->buf) < 0)
>>>
>>> At this point, 'logfile' is still 'sb_logfile->buf', so do you really
>>> need this change?
>>
>> Junio made the same comment last time and I missed it. Will update.
>
> No I will not :-) safe_create_leading_directories takes an editable
> string, but logfile is now a const string. We could use
> s_c_l_d_const() but that one will make a copy of the string
> unncessarily. Will make a note in the commit message though.
Rather than explaining it in the commit message, it might be better
eliminate the source of confusion by taking one of these approaches:
1. Drop the 'const char *logfile' variable altogether; rename the
strbuf argument to 'logfile'; and just use logfile->buf everywhere
'logfile' is used in the current code. This makes the diff a bit more
noisy, but eliminates confusion of reviewers reading the patch.
2. Keep the 'const char *logfile' but assign it just before its first
(real) use in 'logfd = open(logfile...)'. (There's one earlier use in
a diagnostic, but sb_logfile->buf could suffice there.)
3. Just declare it 'char *logfile' and use it everywhere in the
function. This is a bit ugly since it's not obvious to the reviewer
that it is non-const only for the sake of
safe_create_leading_directories().
I fiind myself favoring #1 in this particular case.
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 03/27] path.c: rename vsnpath() to do_git_path()
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 01/27] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 02/27] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 04/27] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
` (24 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The name vsnpath() gives an impression that this is general path
handling function. It's not. This is the underlying implementation of
git_path(), git_pathdup() and strbuf_git_path() which will prefix
$GIT_DIR in the result string.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/path.c b/path.c
index b52a16f..29048fe 100644
--- a/path.c
+++ b/path.c
@@ -60,7 +60,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
-static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
{
const char *git_dir = get_git_dir();
strbuf_addstr(buf, git_dir);
@@ -74,7 +74,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- vsnpath(sb, fmt, args);
+ do_git_path(sb, fmt, args);
va_end(args);
}
@@ -83,7 +83,7 @@ char *git_pathdup(const char *fmt, ...)
struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- vsnpath(&path, fmt, args);
+ do_git_path(&path, fmt, args);
va_end(args);
return strbuf_detach(&path, NULL);
}
@@ -114,7 +114,7 @@ char *git_path(const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- vsnpath(pathname, fmt, args);
+ do_git_path(pathname, fmt, args);
va_end(args);
return pathname->buf;
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 04/27] path.c: group git_path(), git_pathdup() and strbuf_git_path() together
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (2 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 03/27] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 05/27] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
` (23 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/path.c b/path.c
index 29048fe..ccd7228 100644
--- a/path.c
+++ b/path.c
@@ -78,6 +78,16 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
va_end(args);
}
+char *git_path(const char *fmt, ...)
+{
+ struct strbuf *pathname = get_pathname();
+ va_list args;
+ va_start(args, fmt);
+ do_git_path(pathname, fmt, args);
+ va_end(args);
+ return pathname->buf;
+}
+
char *git_pathdup(const char *fmt, ...)
{
struct strbuf path = STRBUF_INIT;
@@ -109,16 +119,6 @@ char *mkpath(const char *fmt, ...)
return cleanup_path(pathname->buf);
}
-char *git_path(const char *fmt, ...)
-{
- struct strbuf *pathname = get_pathname();
- va_list args;
- va_start(args, fmt);
- do_git_path(pathname, fmt, args);
- va_end(args);
- return pathname->buf;
-}
-
void home_config_paths(char **global, char **xdg, char *file)
{
char *xdg_home = getenv("XDG_CONFIG_HOME");
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 05/27] Make git_path() aware of file relocation in $GIT_DIR
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (3 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 04/27] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-03 1:34 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 06/27] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
` (22 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
We allow the user to relocate certain paths out of $GIT_DIR via
environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and
GIT_GRAFT_FILE. All callers are not supposed to use git_path() or
git_pathdup() to get those paths. Instead they must use
get_object_directory(), get_index_file() and get_graft_file()
respectively. This is inconvenient and could be missed in review
(there's git_path("objects/info/alternates") somewhere in
sha1_file.c).
This patch makes git_path() and git_pathdup() understand those
environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar,
git_path("objects/abc") should return /tmp/bar/abc. The same is done
for the two remaining env variables.
"git rev-parse --git-path" is the wrapper for script use.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-rev-parse.txt | 5 +++++
builtin/rev-parse.c | 7 +++++++
cache.h | 1 +
environment.c | 9 ++++++--
path.c | 46 +++++++++++++++++++++++++++++++++++++++++
t/t0060-path-utils.sh | 19 +++++++++++++++++
6 files changed, 85 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 0d2cdcd..33e4e90 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -232,6 +232,11 @@ print a message to stderr and exit with nonzero status.
repository. If <path> is a gitfile then the resolved path
to the real repository is printed.
+--git-path <path>::
+ Resolve "$GIT_DIR/<path>" and takes other path relocation
+ variables such as $GIT_OBJECT_DIRECTORY,
+ $GIT_INDEX_FILE... into account.
+
--show-cdup::
When the command is invoked from a subdirectory, show the
path of the top-level directory relative to the current
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index aaeb611..e50bc65 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -518,6 +518,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
+ if (!strcmp(arg, "--git-path")) {
+ if (!argv[i + 1])
+ die("--git-path requires an argument");
+ puts(git_path("%s", argv[i + 1]));
+ i++;
+ continue;
+ }
if (as_is) {
if (show_file(arg, output_prefix) && as_is < 2)
verify_filename(prefix, arg, 0);
diff --git a/cache.h b/cache.h
index 8d3697e..6c08e4a 100644
--- a/cache.h
+++ b/cache.h
@@ -585,6 +585,7 @@ extern int fsync_object_files;
extern int core_preload_index;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
+extern int git_db_env, git_index_env, git_graft_env;
/*
* The character that begins a commented line in user-editable file
diff --git a/environment.c b/environment.c
index 4a3437d..f513479 100644
--- a/environment.c
+++ b/environment.c
@@ -82,6 +82,7 @@ static size_t namespace_len;
static const char *git_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
+int git_db_env, git_index_env, git_graft_env;
/*
* Repository-local GIT_* environment variables; see cache.h for details.
@@ -137,15 +138,19 @@ static void setup_git_env(void)
if (!git_object_dir) {
git_object_dir = xmalloc(strlen(git_dir) + 9);
sprintf(git_object_dir, "%s/objects", git_dir);
- }
+ } else
+ git_db_env = 1;
git_index_file = getenv(INDEX_ENVIRONMENT);
if (!git_index_file) {
git_index_file = xmalloc(strlen(git_dir) + 7);
sprintf(git_index_file, "%s/index", git_dir);
- }
+ } else
+ git_index_env = 1;
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
git_graft_file = git_pathdup("info/grafts");
+ else
+ git_graft_env = 1;
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
read_replace_refs = 0;
namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
diff --git a/path.c b/path.c
index ccd7228..e020530 100644
--- a/path.c
+++ b/path.c
@@ -60,13 +60,59 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
+static int dir_prefix(const char *buf, const char *dir)
+{
+ int len = strlen(dir);
+ return !strncmp(buf, dir, len) &&
+ (is_dir_sep(buf[len]) || buf[len] == '\0');
+}
+
+/* $buf =~ m|$dir/+$file| but without regex */
+static int is_dir_file(const char *buf, const char *dir, const char *file)
+{
+ int len = strlen(dir);
+ if (strncmp(buf, dir, len) || !is_dir_sep(buf[len]))
+ return 0;
+ while (is_dir_sep(buf[len]))
+ len++;
+ return !strcmp(buf + len, file);
+}
+
+static void replace_dir(struct strbuf *buf, int len, const char *newdir)
+{
+ int newlen = strlen(newdir);
+ int need_sep = (buf->buf[len] && !is_dir_sep(buf->buf[len])) &&
+ !is_dir_sep(newdir[newlen - 1]);
+ if (need_sep)
+ len--; /* keep one char, to be replaced with '/' */
+ strbuf_splice(buf, 0, len, newdir, newlen);
+ if (need_sep)
+ buf->buf[newlen] = '/';
+}
+
+static void adjust_git_path(struct strbuf *buf, int git_dir_len)
+{
+ const char *base = buf->buf + git_dir_len;
+ if (git_graft_env && is_dir_file(base, "info", "grafts"))
+ strbuf_splice(buf, 0, buf->len,
+ get_graft_file(), strlen(get_graft_file()));
+ else if (git_index_env && !strcmp(base, "index"))
+ strbuf_splice(buf, 0, buf->len,
+ get_index_file(), strlen(get_index_file()));
+ else if (git_db_env && dir_prefix(base, "objects"))
+ replace_dir(buf, git_dir_len + 7, get_object_directory());
+}
+
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
{
const char *git_dir = get_git_dir();
+ int gitdir_len;
strbuf_addstr(buf, git_dir);
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
+ gitdir_len = buf->len;
strbuf_vaddf(buf, fmt, args);
+ adjust_git_path(buf, gitdir_len);
strbuf_cleanup_path(buf);
}
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 07c10c8..1d29901 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -19,6 +19,14 @@ relative_path() {
"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
}
+test_git_path() {
+ test_expect_success "git-path $1 $2 => $3" "
+ $1 git rev-parse --git-path $2 >actual &&
+ echo $3 >expect &&
+ test_cmp expect actual
+ "
+}
+
# On Windows, we are using MSYS's bash, which mangles the paths.
# Absolute paths are anchored at the MSYS installation directory,
# which means that the path / accounts for this many characters:
@@ -223,4 +231,15 @@ relative_path "<null>" "<empty>" ./
relative_path "<null>" "<null>" ./
relative_path "<null>" /foo/a/b ./
+test_git_path A=B info/grafts .git/info/grafts
+test_git_path GIT_GRAFT_FILE=foo info/grafts foo
+test_git_path GIT_GRAFT_FILE=foo info/////grafts foo
+test_git_path GIT_INDEX_FILE=foo index foo
+test_git_path GIT_INDEX_FILE=foo index/foo .git/index/foo
+test_git_path GIT_INDEX_FILE=foo index2 .git/index2
+test_expect_success 'setup fake objects directory foo' 'mkdir foo'
+test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 05/27] Make git_path() aware of file relocation in $GIT_DIR
2014-03-01 12:12 ` [PATCH v4 05/27] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-03 1:34 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-03 1:34 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> We allow the user to relocate certain paths out of $GIT_DIR via
> environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and
> GIT_GRAFT_FILE. All callers are not supposed to use git_path() or
"All callers are not" is unusually difficult to understand. Changing
it to "Callers are not" simplifies.
> git_pathdup() to get those paths. Instead they must use
> get_object_directory(), get_index_file() and get_graft_file()
> respectively. This is inconvenient and could be missed in review
> (there's git_path("objects/info/alternates") somewhere in
"(for example, there's..." reads a bit better.
> sha1_file.c).
>
> This patch makes git_path() and git_pathdup() understand those
> environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar,
> git_path("objects/abc") should return /tmp/bar/abc. The same is done
I guess you mean it should return /foo/bar/abc.
> for the two remaining env variables.
>
> "git rev-parse --git-path" is the wrapper for script use.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> Documentation/git-rev-parse.txt | 5 +++++
> builtin/rev-parse.c | 7 +++++++
> cache.h | 1 +
> environment.c | 9 ++++++--
> path.c | 46 +++++++++++++++++++++++++++++++++++++++++
> t/t0060-path-utils.sh | 19 +++++++++++++++++
> 6 files changed, 85 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
> index 0d2cdcd..33e4e90 100644
> --- a/Documentation/git-rev-parse.txt
> +++ b/Documentation/git-rev-parse.txt
> @@ -232,6 +232,11 @@ print a message to stderr and exit with nonzero status.
> repository. If <path> is a gitfile then the resolved path
> to the real repository is printed.
>
> +--git-path <path>::
> + Resolve "$GIT_DIR/<path>" and takes other path relocation
> + variables such as $GIT_OBJECT_DIRECTORY,
> + $GIT_INDEX_FILE... into account.
Would it help to add a quick illustration here?
For example, if GIT_OBJECT_DIRECTORY is /foo/bar,
then "git rev-parse --git-path objects/abc" returns /foo/bar/abc.
> --show-cdup::
> When the command is invoked from a subdirectory, show the
> path of the top-level directory relative to the current
> diff --git a/path.c b/path.c
> index ccd7228..e020530 100644
> --- a/path.c
> +++ b/path.c
> @@ -60,13 +60,59 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
> return cleanup_path(buf);
> }
>
> static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
> {
> const char *git_dir = get_git_dir();
> + int gitdir_len;
> strbuf_addstr(buf, git_dir);
Maybe simplify by dropping git_dir and invoking strbuf_addstr(buf,
get_git_dir())?
> if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
> strbuf_addch(buf, '/');
> + gitdir_len = buf->len;
> strbuf_vaddf(buf, fmt, args);
> + adjust_git_path(buf, gitdir_len);
> strbuf_cleanup_path(buf);
> }
>
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 06/27] *.sh: respect $GIT_INDEX_FILE
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (4 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 05/27] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 07/27] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
` (21 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-pull.sh | 2 +-
git-stash.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-pull.sh b/git-pull.sh
index 0a5aa2c..c9dc9ba 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -218,7 +218,7 @@ test true = "$rebase" && {
if ! git rev-parse -q --verify HEAD >/dev/null
then
# On an unborn branch
- if test -f "$GIT_DIR/index"
+ if test -f "`git rev-parse --git-path index`"
then
die "$(gettext "updating an unborn branch with changes added to the index")"
fi
diff --git a/git-stash.sh b/git-stash.sh
index f0a94ab..ae7d16e 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -20,7 +20,7 @@ require_work_tree
cd_to_toplevel
TMP="$GIT_DIR/.git-stash.$$"
-TMPindex=${GIT_INDEX_FILE-"$GIT_DIR/index"}.stash.$$
+TMPindex=${GIT_INDEX_FILE-"`git rev-parse --git-path index`"}.stash.$$
trap 'rm -f "$TMP-"* "$TMPindex"' 0
ref_stash=refs/stash
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 07/27] reflog: avoid constructing .lock path with git_path
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (5 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 06/27] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 08/27] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
` (20 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
git_path() soon understands the path given to it and can transform the
path instead of just prepending $GIT_DIR. So given path "abc",
git_path() may return "$GIT_DIR/abc". But given path "def", git_path()
may return "$GIT_DIR/ghi".
Giving path "def.lock" to git_path() may confuse it and make it
believe "def.lock" should not be transformed because the signature is
"def.lock" not "def". But we want the lock file to have the same base
name with the locked file (e.g. "ghi.lock", not "def.lock"). It's best
to append ".lock" after git_path() has done its conversion.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/reflog.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 852cff6..ccf2cf6 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -372,7 +372,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
if (!file_exists(log_file))
goto finish;
if (!cmd->dry_run) {
- newlog_path = git_pathdup("logs/%s.lock", ref);
+ newlog_path = mkpathdup("%s.lock", log_file);
cb.newlog = fopen(newlog_path, "w");
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 08/27] fast-import: use git_path() for accessing .git dir instead of get_git_dir()
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (6 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 07/27] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 09/27] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
` (19 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This allows git_path() to redirect info/fast-import to another place
if needed
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
fast-import.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/fast-import.c b/fast-import.c
index 4fd18a3..08a1e78 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -3125,12 +3125,9 @@ static void parse_progress(void)
static char* make_fast_import_path(const char *path)
{
- struct strbuf abs_path = STRBUF_INIT;
-
if (!relative_marks_paths || is_absolute_path(path))
return xstrdup(path);
- strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
- return strbuf_detach(&abs_path, NULL);
+ return xstrdup(git_path("info/fast-import/%s", path));
}
static void option_import_marks(const char *marks,
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 09/27] commit: use SEQ_DIR instead of hardcoding "sequencer"
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (7 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 08/27] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 10/27] Add new environment variable $GIT_COMMON_DIR Nguyễn Thái Ngọc Duy
` (18 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/commit.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/commit.c b/builtin/commit.c
index 3767478..ee3ac10 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -155,7 +155,7 @@ static void determine_whence(struct wt_status *s)
whence = FROM_MERGE;
else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
whence = FROM_CHERRY_PICK;
- if (file_exists(git_path("sequencer")))
+ if (file_exists(git_path(SEQ_DIR)))
sequencer_in_use = 1;
}
else
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 10/27] Add new environment variable $GIT_COMMON_DIR
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (8 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 09/27] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-03 7:29 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 11/27] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
` (17 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This variable is intended to support multiple working directories
attached to a repository. Such a repository may have a main working
directory, created by either "git init" or "git clone" and one or more
linked working directories. These working directories and the main
repository share the same repository directory.
In linked working directories, $GIT_COMMON_DIR must be defined to point
to the real repository directory and $GIT_DIR points to an unused
subdirectory inside $GIT_COMMON_DIR. File locations inside the
repository are reorganized from the linked worktree view point:
- worktree-specific such as HEAD, logs/HEAD, index, other top-level
refs and unrecognized files are from $GIT_DIR.
- the rest like objects, refs, info, hooks, packed-refs, shallow...
are from $GIT_COMMON_DIR
Scripts are supposed to retrieve paths in $GIT_DIR with "git rev-parse
--git-path", which will take care of "$GIT_DIR vs $GIT_COMMON_DIR"
business.
The redirection is done by git_path(), git_pathdup() and
strbuf_git_path(). The selected list of paths goes to $GIT_COMMON_DIR,
not the other way around in case a developer adds a new
worktree-specific file and it's accidentally promoted to be shared
across repositories (this includes unknown files added by third party
commands)
The list of known files that belong to $GIT_DIR are:
ADD_EDIT.patch BISECT_ANCESTORS_OK BISECT_EXPECTED_REV BISECT_LOG
BISECT_NAMES CHERRY_PICK_HEAD COMMIT_MSG FETCH_HEAD HEAD MERGE_HEAD
MERGE_MODE MERGE_RR NOTES_EDITMSG NOTES_MERGE_WORKTREE ORIG_HEAD
REVERT_HEAD SQUASH_MSG TAG_EDITMSG fast_import_crash_* logs/HEAD
next-index-* rebase-apply rebase-merge rsync-refs-* sequencer/*
shallow_*
Path mapping is NOT done for git_path_submodule(). Multi-checkouts are
not supported as submodules.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git.txt | 8 +++++++
Documentation/gitrepository-layout.txt | 42 ++++++++++++++++++++++++++--------
cache.h | 4 +++-
environment.c | 19 +++++++++++----
path.c | 29 +++++++++++++++++++++++
t/t0060-path-utils.sh | 15 ++++++++++++
6 files changed, 103 insertions(+), 14 deletions(-)
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 02bbc08..b094b1f 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -773,6 +773,14 @@ Git so take care if using Cogito etc.
an explicit repository directory set via 'GIT_DIR' or on the
command line.
+'GIT_COMMON_DIR'::
+ If this variable is set to a path, non-worktree files that are
+ normally in $GIT_DIR will be taken from this path
+ instead. Worktree-specific files such as HEAD or index are
+ taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for
+ details. This variable has lower precedence than other path
+ variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
+
Git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index aa03882..10672a1 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -46,6 +46,9 @@ of incomplete object store is not suitable to be published for
use with dumb transports but otherwise is OK as long as
`objects/info/alternates` points at the object stores it
borrows from.
++
+This directory is ignored $GIT_COMMON_DIR is set and
+"$GIT_COMMON_DIR/objects" will be used instead.
objects/[0-9a-f][0-9a-f]::
A newly created object is stored in its own file.
@@ -92,7 +95,8 @@ refs::
References are stored in subdirectories of this
directory. The 'git prune' command knows to preserve
objects reachable from refs found in this directory and
- its subdirectories.
+ its subdirectories. This directory is ignored $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/refs" will be used instead.
refs/heads/`name`::
records tip-of-the-tree commit objects of branch `name`
@@ -114,7 +118,8 @@ refs/replace/`<obj-sha1>`::
packed-refs::
records the same information as refs/heads/, refs/tags/,
and friends record in a more efficient way. See
- linkgit:git-pack-refs[1].
+ linkgit:git-pack-refs[1]. This file is ignored $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/packed-refs" will be used instead.
HEAD::
A symref (see glossary) to the `refs/heads/` namespace
@@ -133,6 +138,11 @@ being a symref to point at the current branch. Such a state
is often called 'detached HEAD.' See linkgit:git-checkout[1]
for details.
+config::
+ Repository specific configuration file. This file is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
+ used instead.
+
branches::
A slightly deprecated way to store shorthands to be used
to specify a URL to 'git fetch', 'git pull' and 'git push'.
@@ -140,7 +150,10 @@ branches::
'name' can be given to these commands in place of
'repository' argument. See the REMOTES section in
linkgit:git-fetch[1] for details. This mechanism is legacy
- and not likely to be found in modern repositories.
+ and not likely to be found in modern repositories. This
+ directory is ignored $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/branches" will be used instead.
+
hooks::
Hooks are customization scripts used by various Git
@@ -149,7 +162,9 @@ hooks::
default. To enable, the `.sample` suffix has to be
removed from the filename by renaming.
Read linkgit:githooks[5] for more details about
- each hook.
+ each hook. This directory is ignored $GIT_COMMON_DIR is set
+ and "$GIT_COMMON_DIR/hooks" will be used instead.
+
index::
The current index file for the repository. It is
@@ -157,7 +172,8 @@ index::
info::
Additional information about the repository is recorded
- in this directory.
+ in this directory. This directory is ignored $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/index" will be used instead.
info/refs::
This file helps dumb transports discover what refs are
@@ -193,12 +209,16 @@ remotes::
when interacting with remote repositories via 'git fetch',
'git pull' and 'git push' commands. See the REMOTES section
in linkgit:git-fetch[1] for details. This mechanism is legacy
- and not likely to be found in modern repositories.
+ and not likely to be found in modern repositories. This
+ directory is ignored $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/remotes" will be used instead.
logs::
Records of changes made to refs are stored in this
directory. See linkgit:git-update-ref[1]
- for more information.
+ for more information. This directory is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/logs" will be used
+ instead.
logs/refs/heads/`name`::
Records all changes made to the branch tip named `name`.
@@ -209,10 +229,14 @@ logs/refs/tags/`name`::
shallow::
This is similar to `info/grafts` but is internally used
and maintained by shallow clone mechanism. See `--depth`
- option to linkgit:git-clone[1] and linkgit:git-fetch[1].
+ option to linkgit:git-clone[1] and linkgit:git-fetch[1]. This
+ file is ignored $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/shallow" will be used instead.
modules::
- Contains the git-repositories of the submodules.
+ Contains the git-repositories of the submodules. This
+ directory is ignored $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/modules" will be used instead.
SEE ALSO
--------
diff --git a/cache.h b/cache.h
index 6c08e4a..51ade32 100644
--- a/cache.h
+++ b/cache.h
@@ -347,6 +347,7 @@ static inline enum object_type object_type(unsigned int mode)
/* Double-check local_repo_env below if you add to this list. */
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
@@ -400,6 +401,7 @@ extern int is_inside_git_dir(void);
extern char *git_work_tree_cfg;
extern int is_inside_work_tree(void);
extern const char *get_git_dir(void);
+extern const char *get_git_common_dir(void);
extern int is_git_directory(const char *path);
extern char *get_object_directory(void);
extern char *get_index_file(void);
@@ -585,7 +587,7 @@ extern int fsync_object_files;
extern int core_preload_index;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
-extern int git_db_env, git_index_env, git_graft_env;
+extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
/*
* The character that begins a commented line in user-editable file
diff --git a/environment.c b/environment.c
index f513479..c998120 100644
--- a/environment.c
+++ b/environment.c
@@ -80,9 +80,9 @@ static char *work_tree;
static const char *namespace;
static size_t namespace_len;
-static const char *git_dir;
+static const char *git_dir, *git_common_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
-int git_db_env, git_index_env, git_graft_env;
+int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
/*
* Repository-local GIT_* environment variables; see cache.h for details.
@@ -134,10 +134,16 @@ static void setup_git_env(void)
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
gitfile = read_gitfile(git_dir);
git_dir = xstrdup(gitfile ? gitfile : git_dir);
+ git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (git_common_dir) {
+ git_common_dir_env = 1;
+ git_common_dir = xstrdup(git_common_dir);
+ } else
+ git_common_dir = git_dir;
git_object_dir = getenv(DB_ENVIRONMENT);
if (!git_object_dir) {
- git_object_dir = xmalloc(strlen(git_dir) + 9);
- sprintf(git_object_dir, "%s/objects", git_dir);
+ git_object_dir = xmalloc(strlen(git_common_dir) + 9);
+ sprintf(git_object_dir, "%s/objects", git_common_dir);
} else
git_db_env = 1;
git_index_file = getenv(INDEX_ENVIRONMENT);
@@ -173,6 +179,11 @@ const char *get_git_dir(void)
return git_dir;
}
+const char *get_git_common_dir(void)
+{
+ return git_common_dir;
+}
+
const char *get_git_namespace(void)
{
if (!namespace)
diff --git a/path.c b/path.c
index e020530..6129026 100644
--- a/path.c
+++ b/path.c
@@ -90,6 +90,33 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
buf->buf[newlen] = '/';
}
+static void update_common_dir(struct strbuf *buf, int git_dir_len)
+{
+ const char *common_dir_list[] = {
+ "branches", "hooks", "info", "logs", "lost-found", "modules",
+ "objects", "refs", "remotes", "rr-cache", "svn",
+ NULL
+ };
+ const char *common_top_file_list[] = {
+ "config", "gc.pid", "packed-refs", "shallow", NULL
+ };
+ char *base = buf->buf + git_dir_len;
+ const char **p;
+
+ if (is_dir_file(base, "logs", "HEAD"))
+ return; /* keep this in $GIT_DIR */
+ for (p = common_dir_list; *p; p++)
+ if (dir_prefix(base, *p)) {
+ replace_dir(buf, git_dir_len, get_git_common_dir());
+ return;
+ }
+ for (p = common_top_file_list; *p; p++)
+ if (!strcmp(base, *p)) {
+ replace_dir(buf, git_dir_len, get_git_common_dir());
+ return;
+ }
+}
+
static void adjust_git_path(struct strbuf *buf, int git_dir_len)
{
const char *base = buf->buf + git_dir_len;
@@ -101,6 +128,8 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
get_index_file(), strlen(get_index_file()));
else if (git_db_env && dir_prefix(base, "objects"))
replace_dir(buf, git_dir_len + 7, get_object_directory());
+ else if (git_common_dir_env)
+ update_common_dir(buf, git_dir_len);
}
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 1d29901..f9a77e4 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -241,5 +241,20 @@ test_expect_success 'setup fake objects directory foo' 'mkdir foo'
test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+test_expect_success 'setup common repository' 'git --git-dir=bar init'
+test_git_path GIT_COMMON_DIR=bar index .git/index
+test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar objects bar/objects
+test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar
+test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude
+test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar
+test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar
+test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me
+test_git_path GIT_COMMON_DIR=bar config bar/config
+test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
+test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 10/27] Add new environment variable $GIT_COMMON_DIR
2014-03-01 12:12 ` [PATCH v4 10/27] Add new environment variable $GIT_COMMON_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-03 7:29 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-03 7:29 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This variable is intended to support multiple working directories
> attached to a repository. Such a repository may have a main working
> directory, created by either "git init" or "git clone" and one or more
> linked working directories. These working directories and the main
> repository share the same repository directory.
> ---
> diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
> index aa03882..10672a1 100644
> --- a/Documentation/gitrepository-layout.txt
> +++ b/Documentation/gitrepository-layout.txt
> @@ -46,6 +46,9 @@ of incomplete object store is not suitable to be published for
> use with dumb transports but otherwise is OK as long as
> `objects/info/alternates` points at the object stores it
> borrows from.
> ++
> +This directory is ignored $GIT_COMMON_DIR is set and
s/ignored \$/ignored if $/g
Note the /g since this error is repeated throughout the rest of the
gitrepository-layout.txt patch.
> +"$GIT_COMMON_DIR/objects" will be used instead.
>
> objects/[0-9a-f][0-9a-f]::
> A newly created object is stored in its own file.
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 11/27] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (9 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 10/27] Add new environment variable $GIT_COMMON_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 12/27] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
` (16 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If $GIT_COMMON_DIR is set, $GIT_OBJECT_DIRECTORY should be
$GIT_COMMON_DIR/objects, not $GIT_DIR/objects. Just let rev-parse
--git-path handle it.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-sh-setup.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index fffa3c7..475ca43 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -343,7 +343,7 @@ then
echo >&2 "Unable to determine absolute path of git directory"
exit 1
}
- : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+ : ${GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)"}
fi
peel_committish () {
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 12/27] *.sh: avoid hardcoding $GIT_DIR/hooks/...
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (10 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 11/27] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-03 8:31 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 13/27] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
` (15 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If $GIT_COMMON_DIR is set, it should be $GIT_COMMON_DIR/hooks/, not
$GIT_DIR/hooks/. Just let rev-parse --git-path handle it.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-am.sh | 22 +++++++++++-----------
git-rebase--interactive.sh | 6 +++---
git-rebase--merge.sh | 6 ++----
git-rebase.sh | 4 ++--
templates/hooks--applypatch-msg.sample | 4 ++--
templates/hooks--pre-applypatch.sample | 4 ++--
6 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/git-am.sh b/git-am.sh
index bbea430..dfa0618 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -803,10 +803,10 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
continue
fi
- if test -x "$GIT_DIR"/hooks/applypatch-msg
+ hook="`git rev-parse --git-path hooks/applypatch-msg`"
+ if test -x "$hook"
then
- "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
- stop_here $this
+ "$hook" "$dotest/final-commit" || stop_here $this
fi
if test -f "$dotest/final-commit"
@@ -880,9 +880,10 @@ did you forget to use 'git add'?"
stop_here_user_resolve $this
fi
- if test -x "$GIT_DIR"/hooks/pre-applypatch
+ hook="`git rev-parse --git-path hooks/pre-applypatch`"
+ if test -x "$hook"
then
- "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+ "$hook" || stop_here $this
fi
tree=$(git write-tree) &&
@@ -908,18 +909,17 @@ did you forget to use 'git add'?"
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
fi
- if test -x "$GIT_DIR"/hooks/post-applypatch
- then
- "$GIT_DIR"/hooks/post-applypatch
- fi
+ hook="`git rev-parse --git-path hooks/post-applypatch`"
+ test -x "$hook" && "$hook"
go_next
done
if test -s "$dotest"/rewritten; then
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
- if test -x "$GIT_DIR"/hooks/post-rewrite; then
- "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+ hook="`git rev-parse --git-path hooks/post-rewrite`"
+ if test -x "$hook"; then
+ "$hook" rebase < "$dotest"/rewritten
fi
fi
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 43c19e0..d741b04 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -632,9 +632,9 @@ do_next () {
git notes copy --for-rewrite=rebase < "$rewritten_list" ||
true # we don't care if this copying failed
} &&
- if test -x "$GIT_DIR"/hooks/post-rewrite &&
- test -s "$rewritten_list"; then
- "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
+ hook="`git rev-parse --git-path hooks/post-rewrite`"
+ if test -x "$hook" && test -s "$rewritten_list"; then
+ "$hook" rebase < "$rewritten_list"
true # we don't care if this hook failed
fi &&
warn "Successfully rebased and updated $head_name."
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index e7d96de..68f5d09 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -93,10 +93,8 @@ finish_rb_merge () {
if test -s "$state_dir"/rewritten
then
git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
- if test -x "$GIT_DIR"/hooks/post-rewrite
- then
- "$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
- fi
+ hook="`git rev-parse --git-path hooks/post-rewrite`"
+ test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
fi
say All done.
}
diff --git a/git-rebase.sh b/git-rebase.sh
index 8a3efa2..1cf8dba 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -195,9 +195,9 @@ run_specific_rebase () {
run_pre_rebase_hook () {
if test -z "$ok_to_skip_pre_rebase" &&
- test -x "$GIT_DIR/hooks/pre-rebase"
+ test -x "`git rev-parse --git-path hooks/pre-rebase`"
then
- "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+ "`git rev-parse --git-path hooks/pre-rebase`" ${1+"$@"} ||
die "$(gettext "The pre-rebase hook refused to rebase.")"
fi
}
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
index 8b2a2fe..28b843b 100755
--- a/templates/hooks--applypatch-msg.sample
+++ b/templates/hooks--applypatch-msg.sample
@@ -10,6 +10,6 @@
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
-test -x "$GIT_DIR/hooks/commit-msg" &&
- exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+commitmsg="`git rev-parse --git-path hooks/commit-msg`"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
index b1f187c..51aa244 100755
--- a/templates/hooks--pre-applypatch.sample
+++ b/templates/hooks--pre-applypatch.sample
@@ -9,6 +9,6 @@
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
-test -x "$GIT_DIR/hooks/pre-commit" &&
- exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+precommit="`git rev-parse --git-path hooks/pre-commit`"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 12/27] *.sh: avoid hardcoding $GIT_DIR/hooks/...
2014-03-01 12:12 ` [PATCH v4 12/27] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
@ 2014-03-03 8:31 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-03 8:31 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> If $GIT_COMMON_DIR is set, it should be $GIT_COMMON_DIR/hooks/, not
> $GIT_DIR/hooks/. Just let rev-parse --git-path handle it.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> git-am.sh | 22 +++++++++++-----------
> git-rebase--interactive.sh | 6 +++---
> git-rebase--merge.sh | 6 ++----
> git-rebase.sh | 4 ++--
> templates/hooks--applypatch-msg.sample | 4 ++--
> templates/hooks--pre-applypatch.sample | 4 ++--
> 6 files changed, 22 insertions(+), 24 deletions(-)
>
> diff --git a/git-am.sh b/git-am.sh
> index bbea430..dfa0618 100755
> --- a/git-am.sh
> +++ b/git-am.sh
> @@ -803,10 +803,10 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
> continue
> fi
>
> - if test -x "$GIT_DIR"/hooks/applypatch-msg
> + hook="`git rev-parse --git-path hooks/applypatch-msg`"
Did you want to use $(...) rather than `...`?
Same question for the remainder of the patch.
> + if test -x "$hook"
> then
> - "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
> - stop_here $this
> + "$hook" "$dotest/final-commit" || stop_here $this
> fi
>
> if test -f "$dotest/final-commit"
> @@ -880,9 +880,10 @@ did you forget to use 'git add'?"
> stop_here_user_resolve $this
> fi
>
> - if test -x "$GIT_DIR"/hooks/pre-applypatch
> + hook="`git rev-parse --git-path hooks/pre-applypatch`"
> + if test -x "$hook"
> then
> - "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
> + "$hook" || stop_here $this
> fi
>
> tree=$(git write-tree) &&
> @@ -908,18 +909,17 @@ did you forget to use 'git add'?"
> echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
> fi
>
> - if test -x "$GIT_DIR"/hooks/post-applypatch
> - then
> - "$GIT_DIR"/hooks/post-applypatch
> - fi
> + hook="`git rev-parse --git-path hooks/post-applypatch`"
> + test -x "$hook" && "$hook"
>
> go_next
> done
>
> if test -s "$dotest"/rewritten; then
> git notes copy --for-rewrite=rebase < "$dotest"/rewritten
> - if test -x "$GIT_DIR"/hooks/post-rewrite; then
> - "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
> + hook="`git rev-parse --git-path hooks/post-rewrite`"
> + if test -x "$hook"; then
> + "$hook" rebase < "$dotest"/rewritten
> fi
> fi
>
> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> index 43c19e0..d741b04 100644
> --- a/git-rebase--interactive.sh
> +++ b/git-rebase--interactive.sh
> @@ -632,9 +632,9 @@ do_next () {
> git notes copy --for-rewrite=rebase < "$rewritten_list" ||
> true # we don't care if this copying failed
> } &&
> - if test -x "$GIT_DIR"/hooks/post-rewrite &&
> - test -s "$rewritten_list"; then
> - "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
> + hook="`git rev-parse --git-path hooks/post-rewrite`"
> + if test -x "$hook" && test -s "$rewritten_list"; then
> + "$hook" rebase < "$rewritten_list"
> true # we don't care if this hook failed
> fi &&
> warn "Successfully rebased and updated $head_name."
> diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
> index e7d96de..68f5d09 100644
> --- a/git-rebase--merge.sh
> +++ b/git-rebase--merge.sh
> @@ -93,10 +93,8 @@ finish_rb_merge () {
> if test -s "$state_dir"/rewritten
> then
> git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
> - if test -x "$GIT_DIR"/hooks/post-rewrite
> - then
> - "$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
> - fi
> + hook="`git rev-parse --git-path hooks/post-rewrite`"
> + test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
> fi
> say All done.
> }
> diff --git a/git-rebase.sh b/git-rebase.sh
> index 8a3efa2..1cf8dba 100755
> --- a/git-rebase.sh
> +++ b/git-rebase.sh
> @@ -195,9 +195,9 @@ run_specific_rebase () {
>
> run_pre_rebase_hook () {
> if test -z "$ok_to_skip_pre_rebase" &&
> - test -x "$GIT_DIR/hooks/pre-rebase"
> + test -x "`git rev-parse --git-path hooks/pre-rebase`"
> then
> - "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
> + "`git rev-parse --git-path hooks/pre-rebase`" ${1+"$@"} ||
> die "$(gettext "The pre-rebase hook refused to rebase.")"
> fi
> }
> diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
> index 8b2a2fe..28b843b 100755
> --- a/templates/hooks--applypatch-msg.sample
> +++ b/templates/hooks--applypatch-msg.sample
> @@ -10,6 +10,6 @@
> # To enable this hook, rename this file to "applypatch-msg".
>
> . git-sh-setup
> -test -x "$GIT_DIR/hooks/commit-msg" &&
> - exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
> +commitmsg="`git rev-parse --git-path hooks/commit-msg`"
> +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
> :
> diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
> index b1f187c..51aa244 100755
> --- a/templates/hooks--pre-applypatch.sample
> +++ b/templates/hooks--pre-applypatch.sample
> @@ -9,6 +9,6 @@
> # To enable this hook, rename this file to "pre-applypatch".
>
> . git-sh-setup
> -test -x "$GIT_DIR/hooks/pre-commit" &&
> - exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
> +precommit="`git rev-parse --git-path hooks/pre-commit`"
> +test -x "$precommit" && exec "$precommit" ${1+"$@"}
> :
> --
> 1.9.0.40.gaa8c3ea
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 13/27] git-stash: avoid hardcoding $GIT_DIR/logs/....
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (11 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 12/27] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 15:50 ` Torsten Bögershausen
2014-03-01 12:12 ` [PATCH v4 14/27] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
` (14 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-stash.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-stash.sh b/git-stash.sh
index ae7d16e..12d9b37 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -183,7 +183,7 @@ store_stash () {
fi
# Make sure the reflog for stash is kept.
- : >>"$GIT_DIR/logs/$ref_stash"
+ : >>"`git rev-parse --git-path logs/$ref_stash`"
git update-ref -m "$stash_msg" $ref_stash $w_commit
ret=$?
test $ret != 0 && test -z $quiet &&
@@ -258,7 +258,7 @@ save_stash () {
say "$(gettext "No local changes to save")"
exit 0
fi
- test -f "$GIT_DIR/logs/$ref_stash" ||
+ test -f "`git rev-parse --git-path logs/$ref_stash`" ||
clear_stash || die "$(gettext "Cannot initialize stash")"
create_stash "$stash_msg" $untracked
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 13/27] git-stash: avoid hardcoding $GIT_DIR/logs/....
2014-03-01 12:12 ` [PATCH v4 13/27] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
@ 2014-03-01 15:50 ` Torsten Bögershausen
0 siblings, 0 replies; 169+ messages in thread
From: Torsten Bögershausen @ 2014-03-01 15:50 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano
On 2014-03-01 13.12, Nguyễn Thái Ngọc Duy wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> git-stash.sh | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/git-stash.sh b/git-stash.sh
> index ae7d16e..12d9b37 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -183,7 +183,7 @@ store_stash () {
> fi
>
> # Make sure the reflog for stash is kept.
> - : >>"$GIT_DIR/logs/$ref_stash"
> + : >>"`git rev-parse --git-path logs/$ref_stash`"
"$(git rev-parse --git-path logs/$ref_stash)"
Shouldn't we prefer $() over `` ?
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 14/27] setup.c: convert is_git_directory() to use strbuf
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (12 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 13/27] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 15/27] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
` (13 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/setup.c b/setup.c
index 6c3f85f..4994437 100644
--- a/setup.c
+++ b/setup.c
@@ -184,31 +184,36 @@ void verify_non_filename(const char *prefix, const char *arg)
*/
int is_git_directory(const char *suspect)
{
- char path[PATH_MAX];
- size_t len = strlen(suspect);
+ struct strbuf path = STRBUF_INIT;
+ int ret = 0;
+ size_t len;
- if (PATH_MAX <= len + strlen("/objects"))
- die("Too long path: %.*s", 60, suspect);
- strcpy(path, suspect);
+ strbuf_addstr(&path, suspect);
+ len = path.len;
if (getenv(DB_ENVIRONMENT)) {
if (access(getenv(DB_ENVIRONMENT), X_OK))
- return 0;
+ goto done;
}
else {
- strcpy(path + len, "/objects");
- if (access(path, X_OK))
- return 0;
+ strbuf_addstr(&path, "/objects");
+ if (access(path.buf, X_OK))
+ goto done;
}
- strcpy(path + len, "/refs");
- if (access(path, X_OK))
- return 0;
+ strbuf_setlen(&path, len);
+ strbuf_addstr(&path, "/refs");
+ if (access(path.buf, X_OK))
+ goto done;
- strcpy(path + len, "/HEAD");
- if (validate_headref(path))
- return 0;
+ strbuf_setlen(&path, len);
+ strbuf_addstr(&path, "/HEAD");
+ if (validate_headref(path.buf))
+ goto done;
- return 1;
+ ret = 1;
+done:
+ strbuf_release(&path);
+ return ret;
}
int is_inside_git_dir(void)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 15/27] setup.c: detect $GIT_COMMON_DIR in is_git_directory()
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (13 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 14/27] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 16/27] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
` (12 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If the file "$GIT_DIR/commondir" exists, it contains the value of
$GIT_COMMON_DIR.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/gitrepository-layout.txt | 7 ++++++
setup.c | 43 +++++++++++++++++++++++++++++-----
2 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 10672a1..f72ce75 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -233,6 +233,13 @@ shallow::
file is ignored $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/shallow" will be used instead.
+commondir::
+ If this file exists, $GIT_COMMON_DIR (see linkgit:git[1]) will
+ be set to the path specified in this file if it is not
+ explicitly set. If the specified path is relative, it is
+ relative to $GIT_DIR. The repository with commondir is
+ incomplete without the repository pointed by "commondir".
+
modules::
Contains the git-repositories of the submodules. This
directory is ignored $GIT_COMMON_DIR is set and
diff --git a/setup.c b/setup.c
index 4994437..7e5b334 100644
--- a/setup.c
+++ b/setup.c
@@ -170,6 +170,33 @@ void verify_non_filename(const char *prefix, const char *arg)
"'git <command> [<revision>...] -- [<file>...]'", arg);
}
+static void get_common_dir(struct strbuf *sb, const char *gitdir)
+{
+ struct strbuf data = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (git_common_dir) {
+ strbuf_addstr(sb, git_common_dir);
+ return;
+ }
+ strbuf_addf(&path, "%s/commondir", gitdir);
+ if (file_exists(path.buf)) {
+ if (strbuf_read_file(&data, path.buf, 0) <= 0)
+ die_errno(_("failed to read %s"), path.buf);
+ while (data.len && (data.buf[data.len - 1] == '\n' ||
+ data.buf[data.len - 1] == '\r'))
+ data.len--;
+ data.buf[data.len] = '\0';
+ strbuf_reset(&path);
+ if (!is_absolute_path(data.buf))
+ strbuf_addf(&path, "%s/", gitdir);
+ strbuf_addbuf(&path, &data);
+ strbuf_addstr(sb, real_path(path.buf));
+ } else
+ strbuf_addstr(sb, gitdir);
+ strbuf_release(&data);
+ strbuf_release(&path);
+}
/*
* Test if it looks like we're at a git directory.
@@ -188,13 +215,22 @@ int is_git_directory(const char *suspect)
int ret = 0;
size_t len;
- strbuf_addstr(&path, suspect);
+ /* Check worktree-related signatures */
+ strbuf_addf(&path, "%s/HEAD", suspect);
+ if (validate_headref(path.buf))
+ goto done;
+
+ strbuf_reset(&path);
+ get_common_dir(&path, suspect);
len = path.len;
+
+ /* Check non-worktree-related signatures */
if (getenv(DB_ENVIRONMENT)) {
if (access(getenv(DB_ENVIRONMENT), X_OK))
goto done;
}
else {
+ strbuf_setlen(&path, len);
strbuf_addstr(&path, "/objects");
if (access(path.buf, X_OK))
goto done;
@@ -205,11 +241,6 @@ int is_git_directory(const char *suspect)
if (access(path.buf, X_OK))
goto done;
- strbuf_setlen(&path, len);
- strbuf_addstr(&path, "/HEAD");
- if (validate_headref(path.buf))
- goto done;
-
ret = 1;
done:
strbuf_release(&path);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 16/27] setup.c: convert check_repository_format_gently to use strbuf
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (14 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 15/27] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 17/27] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
` (11 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/setup.c b/setup.c
index 7e5b334..5085ab1 100644
--- a/setup.c
+++ b/setup.c
@@ -288,7 +288,9 @@ void setup_work_tree(void)
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
{
- char repo_config[PATH_MAX+1];
+ struct strbuf sb = STRBUF_INIT;
+ const char *repo_config;
+ int ret = 0;
/*
* git_config() can't be used here because it calls git_pathdup()
@@ -299,7 +301,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
+ strbuf_addf(&sb, "%s/config", gitdir);
+ repo_config = sb.buf;
git_config_early(check_repository_format_version, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
@@ -309,9 +312,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
GIT_REPO_VERSION, repository_format_version);
warning("Please upgrade Git");
*nongit_ok = -1;
- return -1;
+ ret = -1;
}
- return 0;
+ strbuf_release(&sb);
+ return ret;
}
/*
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 17/27] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently()
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (15 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 16/27] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 18/27] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
` (10 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/setup.c b/setup.c
index 5085ab1..42849f3 100644
--- a/setup.c
+++ b/setup.c
@@ -292,6 +292,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
const char *repo_config;
int ret = 0;
+ get_common_dir(&sb, gitdir);
+ strbuf_addstr(&sb, "/config");
+ repo_config = sb.buf;
+
/*
* git_config() can't be used here because it calls git_pathdup()
* to get $GIT_CONFIG/config. That call will make setup_git_env()
@@ -301,8 +305,6 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- strbuf_addf(&sb, "%s/config", gitdir);
- repo_config = sb.buf;
git_config_early(check_repository_format_version, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 18/27] setup.c: support multi-checkout repo setup
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (16 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 17/27] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-05 19:42 ` Junio C Hamano
2014-03-01 12:12 ` [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
` (9 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The repo setup procedure is updated to detect $GIT_DIR/commondir and
set $GIT_COMMON_DIR properly.
The core.worktree is ignored when $GIT_DIR/commondir presents. This is
because "commondir" repos are intended for separate/linked checkouts
and pointing them back to a fixed core.worktree just does not make
sense.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 2 ++
Documentation/git-rev-parse.txt | 3 ++
builtin/rev-parse.c | 4 +++
cache.h | 1 +
environment.c | 8 ++---
setup.c | 33 +++++++++++++-----
t/t1501-worktree.sh | 76 +++++++++++++++++++++++++++++++++++++++++
t/t1510-repo-setup.sh | 1 +
trace.c | 1 +
9 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5f4d793..313d4b3 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -381,6 +381,8 @@ false), while all other repositories are assumed to be bare (bare
core.worktree::
Set the path to the root of the working tree.
+ If GIT_COMMON_DIR environment variable is set, core.worktree
+ is ignored and not used for determining the root of working tree.
This can be overridden by the GIT_WORK_TREE environment
variable and the '--work-tree' command line option.
The value can be an absolute path or relative to the path to
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 33e4e90..8e6ad32 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -215,6 +215,9 @@ If `$GIT_DIR` is not defined and the current directory
is not detected to lie in a Git repository or work tree
print a message to stderr and exit with nonzero status.
+--git-common-dir::
+ Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
+
--is-inside-git-dir::
When the current working directory is below the repository
directory print "true", otherwise "false".
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index e50bc65..c7057ce 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -744,6 +744,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
continue;
}
+ if (!strcmp(arg, "--git-common-dir")) {
+ puts(get_git_common_dir());
+ continue;
+ }
if (!strcmp(arg, "--resolve-git-dir")) {
const char *gitdir = resolve_gitdir(argv[i+1]);
if (!gitdir)
diff --git a/cache.h b/cache.h
index 51ade32..98b5dd3 100644
--- a/cache.h
+++ b/cache.h
@@ -407,6 +407,7 @@ extern char *get_object_directory(void);
extern char *get_index_file(void);
extern char *get_graft_file(void);
extern int set_git_dir(const char *path);
+extern int get_common_dir(struct strbuf *sb, const char *gitdir);
extern const char *get_git_namespace(void);
extern const char *strip_namespace(const char *namespaced_ref);
extern const char *get_git_work_tree(void);
diff --git a/environment.c b/environment.c
index c998120..0999fc1 100644
--- a/environment.c
+++ b/environment.c
@@ -126,6 +126,7 @@ static char *expand_namespace(const char *raw_namespace)
static void setup_git_env(void)
{
+ struct strbuf sb = STRBUF_INIT;
const char *gitfile;
const char *shallow_file;
@@ -134,12 +135,9 @@ static void setup_git_env(void)
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
gitfile = read_gitfile(git_dir);
git_dir = xstrdup(gitfile ? gitfile : git_dir);
- git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
- if (git_common_dir) {
+ if (get_common_dir(&sb, git_dir))
git_common_dir_env = 1;
- git_common_dir = xstrdup(git_common_dir);
- } else
- git_common_dir = git_dir;
+ git_common_dir = strbuf_detach(&sb, NULL);
git_object_dir = getenv(DB_ENVIRONMENT);
if (!git_object_dir) {
git_object_dir = xmalloc(strlen(git_common_dir) + 9);
diff --git a/setup.c b/setup.c
index 42849f3..40ce191 100644
--- a/setup.c
+++ b/setup.c
@@ -170,14 +170,15 @@ void verify_non_filename(const char *prefix, const char *arg)
"'git <command> [<revision>...] -- [<file>...]'", arg);
}
-static void get_common_dir(struct strbuf *sb, const char *gitdir)
+int get_common_dir(struct strbuf *sb, const char *gitdir)
{
struct strbuf data = STRBUF_INIT;
struct strbuf path = STRBUF_INIT;
const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ int ret = 0;
if (git_common_dir) {
strbuf_addstr(sb, git_common_dir);
- return;
+ return 1;
}
strbuf_addf(&path, "%s/commondir", gitdir);
if (file_exists(path.buf)) {
@@ -192,10 +193,12 @@ static void get_common_dir(struct strbuf *sb, const char *gitdir)
strbuf_addf(&path, "%s/", gitdir);
strbuf_addbuf(&path, &data);
strbuf_addstr(sb, real_path(path.buf));
+ ret = 1;
} else
strbuf_addstr(sb, gitdir);
strbuf_release(&data);
strbuf_release(&path);
+ return ret;
}
/*
@@ -286,13 +289,26 @@ void setup_work_tree(void)
initialized = 1;
}
+static int check_repo_format(const char *var, const char *value, void *cb)
+{
+ if (strcmp(var, "core.repositoryformatversion") == 0)
+ repository_format_version = git_config_int(var, value);
+ else if (strcmp(var, "core.sharedrepository") == 0)
+ shared_repository = git_config_perm(var, value);
+ return 0;
+}
+
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
{
struct strbuf sb = STRBUF_INIT;
const char *repo_config;
+ config_fn_t fn;
int ret = 0;
- get_common_dir(&sb, gitdir);
+ if (get_common_dir(&sb, gitdir))
+ fn = check_repo_format;
+ else
+ fn = check_repository_format_version;
strbuf_addstr(&sb, "/config");
repo_config = sb.buf;
@@ -305,7 +321,7 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- git_config_early(check_repository_format_version, NULL, repo_config);
+ git_config_early(fn, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
die ("Expected git repo version <= %d, found %d",
@@ -777,11 +793,10 @@ int git_config_perm(const char *var, const char *value)
int check_repository_format_version(const char *var, const char *value, void *cb)
{
- if (strcmp(var, "core.repositoryformatversion") == 0)
- repository_format_version = git_config_int(var, value);
- else if (strcmp(var, "core.sharedrepository") == 0)
- shared_repository = git_config_perm(var, value);
- else if (strcmp(var, "core.bare") == 0) {
+ int ret = check_repo_format(var, value, cb);
+ if (ret)
+ return ret;
+ if (strcmp(var, "core.bare") == 0) {
is_bare_repository_cfg = git_config_bool(var, value);
if (is_bare_repository_cfg == 1)
inside_work_tree = -1;
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index 8f36aa9..2ac4424 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -346,4 +346,80 @@ test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
test_cmp expected actual
'
+test_expect_success 'Multi-worktree setup' '
+ mkdir work &&
+ mkdir -p repo.git/repos/foo &&
+ cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
+ sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'GIT_DIR set (1)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'GIT_DIR set (2)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo "$TRASH_DIRECTORY/repo.git" >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Auto discovery' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data1 &&
+ git add data1 &&
+ git ls-files --full-name :/ | grep data1 >actual &&
+ echo work/data1 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_DIR/common overrides core.worktree' '
+ mkdir elsewhere &&
+ git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data2 &&
+ git add data2 &&
+ git ls-files --full-name :/ | grep data2 >actual &&
+ echo work/data2 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ echo haha >data3 &&
+ git --git-dir=../.git --work-tree=. add data3 &&
+ git ls-files --full-name -- :/ | grep data3 >actual &&
+ echo data3 >expect &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index cf2ee78..c211ef5 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -106,6 +106,7 @@ setup_env () {
expect () {
cat >"$1/expected" <<-EOF
setup: git_dir: $2
+ setup: git_common_dir: $2
setup: worktree: $3
setup: cwd: $4
setup: prefix: $5
diff --git a/trace.c b/trace.c
index 08180a9..a594761 100644
--- a/trace.c
+++ b/trace.c
@@ -173,6 +173,7 @@ void trace_repo_setup(const char *prefix)
prefix = "(null)";
trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+ trace_printf_key(key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_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));
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 18/27] setup.c: support multi-checkout repo setup
2014-03-01 12:12 ` [PATCH v4 18/27] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
@ 2014-03-05 19:42 ` Junio C Hamano
2014-03-08 1:55 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Junio C Hamano @ 2014-03-05 19:42 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> core.worktree::
> Set the path to the root of the working tree.
> + If GIT_COMMON_DIR environment variable is set, core.worktree
> + is ignored and not used for determining the root of working tree.
Just thinking aloud to see if I got the full implication of the
above right...
If we find ourselves in the multi-checkout mode because we saw
.git/commondir on the filesystem, it is clear that the root of the
working tree is the parent directory of that .git directory.
If the reason we think we are in the multi-checkout mode is not
because of .git/commondir but because $GIT_COMMON_DIR is set, should
we assume the same relationship between the root of the working tree
and the GIT_DIR (however we find it) when the environment variable
$GIT_WORK_TREE is not set? Or should that configuration be an error?
With $GIT_DIR set without $GIT_WORK_TREE set, the user is telling us
that the $cwd is the root of the working tree, so perhaps we should
do the same?
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 18/27] setup.c: support multi-checkout repo setup
2014-03-05 19:42 ` Junio C Hamano
@ 2014-03-08 1:55 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-08 1:55 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
On Thu, Mar 6, 2014 at 2:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
>
>> core.worktree::
>> Set the path to the root of the working tree.
>> + If GIT_COMMON_DIR environment variable is set, core.worktree
>> + is ignored and not used for determining the root of working tree.
>
> Just thinking aloud to see if I got the full implication of the
> above right...
>
> If we find ourselves in the multi-checkout mode because we saw
> .git/commondir on the filesystem, it is clear that the root of the
> working tree is the parent directory of that .git directory.
>
> If the reason we think we are in the multi-checkout mode is not
> because of .git/commondir but because $GIT_COMMON_DIR is set,
I tend to think so, .git/commondir is just a convenient way to set
$GIT_COMMON_DIR. $GIT_COMMON_DIR is the key. config.txt correctly
states so, but the commit message is misleading.
> should we assume the same relationship between the root of the working tree
> and the GIT_DIR (however we find it) when the environment variable
> $GIT_WORK_TREE is not set? Or should that configuration be an error?
> With $GIT_DIR set without $GIT_WORK_TREE set, the user is telling us
> that the $cwd is the root of the working tree, so perhaps we should
> do the same?
It should work exactly like how normal worktree does, so if $GIT_DIR
is set and $GIT_WORK_TREE is not, $PWD is the worktree root.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (17 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 18/27] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 17:11 ` Torsten Bögershausen
2014-03-01 12:12 ` [PATCH v4 20/27] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
` (8 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
cache.h | 2 ++
wrapper.c | 31 +++++++++++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/cache.h b/cache.h
index 98b5dd3..99b86d9 100644
--- a/cache.h
+++ b/cache.h
@@ -1239,6 +1239,8 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
{
return write_in_full(fd, str, strlen(str));
}
+__attribute__((format (printf,3,4)))
+extern int write_file(const char *path, int fatal, const char *fmt, ...);
/* pager.c */
extern void setup_pager(void);
diff --git a/wrapper.c b/wrapper.c
index 0cc5636..5ced50d 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -455,3 +455,34 @@ struct passwd *xgetpwuid_self(void)
errno ? strerror(errno) : _("no such user"));
return pw;
}
+
+int write_file(const char *path, int fatal, const char *fmt, ...)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ va_list params;
+ if (fd < 0) {
+ if (fatal)
+ die_errno(_("could not open %s for writing"), path);
+ return -1;
+ }
+ va_start(params, fmt);
+ strbuf_vaddf(&sb, fmt, params);
+ va_end(params);
+ if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
+ int err = errno;
+ close(fd);
+ errno = err;
+ strbuf_release(&sb);
+ if (fatal)
+ die_errno(_("could not write to %s"), path);
+ return -1;
+ }
+ strbuf_release(&sb);
+ if (close(fd)) {
+ if (fatal)
+ die_errno(_("could not close %s"), path);
+ return -1;
+ }
+ return 0;
+}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close
2014-03-01 12:12 ` [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
@ 2014-03-01 17:11 ` Torsten Bögershausen
2014-03-04 2:47 ` Eric Sunshine
0 siblings, 1 reply; 169+ messages in thread
From: Torsten Bögershausen @ 2014-03-01 17:11 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano
On 2014-03-01 13.12, Nguyễn Thái Ngọc Duy wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> cache.h | 2 ++
> wrapper.c | 31 +++++++++++++++++++++++++++++++
> 2 files changed, 33 insertions(+)
>
> diff --git a/cache.h b/cache.h
> index 98b5dd3..99b86d9 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -1239,6 +1239,8 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
> {
> return write_in_full(fd, str, strlen(str));
> }
> +__attribute__((format (printf,3,4)))
> +extern int write_file(const char *path, int fatal, const char *fmt, ...);
>
> /* pager.c */
> extern void setup_pager(void);
> diff --git a/wrapper.c b/wrapper.c
> index 0cc5636..5ced50d 100644
> --- a/wrapper.c
> +++ b/wrapper.c
> @@ -455,3 +455,34 @@ struct passwd *xgetpwuid_self(void)
> errno ? strerror(errno) : _("no such user"));
> return pw;
> }
> +
> +int write_file(const char *path, int fatal, const char *fmt, ...)
> +{
> + struct strbuf sb = STRBUF_INIT;
> + int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
> + va_list params;
> + if (fd < 0) {
> + if (fatal)
> + die_errno(_("could not open %s for writing"), path);
> + return -1;
> + }
> + va_start(params, fmt);
> + strbuf_vaddf(&sb, fmt, params);
> + va_end(params);
> + if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
> + int err = errno;
> + close(fd);
> + errno = err;
> + strbuf_release(&sb);
Micro nit:
Today we now what strbuf_release() is doing, but if we ever change the
implementation, it is 3% safer to keep err a little bit longer like this:
> + strbuf_release(&sb);
> + errno = err;
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close
2014-03-01 17:11 ` Torsten Bögershausen
@ 2014-03-04 2:47 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-04 2:47 UTC (permalink / raw)
To: Torsten Bögershausen
Cc: Nguyễn Thái Ngọc Duy, Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 12:11 PM, Torsten Bögershausen <tboegi@web.de> wrote:
> On 2014-03-01 13.12, Nguyễn Thái Ngọc Duy wrote:
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> cache.h | 2 ++
>> wrapper.c | 31 +++++++++++++++++++++++++++++++
>> 2 files changed, 33 insertions(+)
>>
>> diff --git a/cache.h b/cache.h
>> index 98b5dd3..99b86d9 100644
>> --- a/cache.h
>> +++ b/cache.h
>> @@ -1239,6 +1239,8 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
>> {
>> return write_in_full(fd, str, strlen(str));
>> }
>> +__attribute__((format (printf,3,4)))
>> +extern int write_file(const char *path, int fatal, const char *fmt, ...);
>>
>> /* pager.c */
>> extern void setup_pager(void);
>> diff --git a/wrapper.c b/wrapper.c
>> index 0cc5636..5ced50d 100644
>> --- a/wrapper.c
>> +++ b/wrapper.c
>> @@ -455,3 +455,34 @@ struct passwd *xgetpwuid_self(void)
>> errno ? strerror(errno) : _("no such user"));
>> return pw;
>> }
>> +
>> +int write_file(const char *path, int fatal, const char *fmt, ...)
>> +{
>> + struct strbuf sb = STRBUF_INIT;
>> + int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
>> + va_list params;
>> + if (fd < 0) {
Micro nit atop Torsten's micro nit:
It is 3% easier to understand the code if the check for open() failure
immediately follows the open() attempt:
va_list params;
int fd = open(...);
if (fd < 0) {
>> + if (fatal)
>> + die_errno(_("could not open %s for writing"), path);
>> + return -1;
>> + }
>> + va_start(params, fmt);
>> + strbuf_vaddf(&sb, fmt, params);
>> + va_end(params);
>> + if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
>> + int err = errno;
>> + close(fd);
>> + errno = err;
>> + strbuf_release(&sb);
> Micro nit:
> Today we now what strbuf_release() is doing, but if we ever change the
> implementation, it is 3% safer to keep err a little bit longer like this:
>> + strbuf_release(&sb);
>> + errno = err;
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 20/27] use new wrapper write_file() for simple file writing
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (18 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 19/27] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 21/27] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
` (7 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This fixes common problems in these code about error handling,
forgetting to close the file handle after fprintf() fails, or not
printing out the error string..
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/branch.c | 4 +---
builtin/init-db.c | 7 +------
daemon.c | 11 +----------
submodule.c | 9 ++-------
transport.c | 8 +++-----
5 files changed, 8 insertions(+), 31 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index b4d7716..3eebdbc 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -754,7 +754,6 @@ static const char edit_description[] = "BRANCH_DESCRIPTION";
static int edit_branch_description(const char *branch_name)
{
- FILE *fp;
int status;
struct strbuf buf = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
@@ -767,8 +766,7 @@ static int edit_branch_description(const char *branch_name)
" %s\n"
"Lines starting with '%c' will be stripped.\n",
branch_name, comment_line_char);
- fp = fopen(git_path(edit_description), "w");
- if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+ if (write_file(git_path(edit_description), 0, "%s", buf.buf)) {
strbuf_release(&buf);
return error(_("could not write branch description template: %s"),
strerror(errno));
diff --git a/builtin/init-db.c b/builtin/init-db.c
index c7c76bb..081e512 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -342,7 +342,6 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
static void separate_git_dir(const char *git_dir)
{
struct stat st;
- FILE *fp;
if (!stat(git_link, &st)) {
const char *src;
@@ -358,11 +357,7 @@ static void separate_git_dir(const char *git_dir)
die_errno(_("unable to move %s to %s"), src, git_dir);
}
- fp = fopen(git_link, "w");
- if (!fp)
- die(_("Could not create git link %s"), git_link);
- fprintf(fp, "gitdir: %s\n", git_dir);
- fclose(fp);
+ write_file(git_link, 1, "gitdir: %s\n", git_dir);
}
int init_db(const char *template_dir, unsigned int flags)
diff --git a/daemon.c b/daemon.c
index 503e039..b880d30 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1122,15 +1122,6 @@ static void daemonize(void)
}
#endif
-static void store_pid(const char *path)
-{
- FILE *f = fopen(path, "w");
- if (!f)
- die_errno("cannot open pid file '%s'", path);
- if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
- die_errno("failed to write pid file '%s'", path);
-}
-
static int serve(struct string_list *listen_addr, int listen_port,
struct credentials *cred)
{
@@ -1339,7 +1330,7 @@ int main(int argc, char **argv)
sanitize_stdfds();
if (pid_file)
- store_pid(pid_file);
+ write_file(pid_file, 1, "%"PRIuMAX"\n", (uintmax_t) getpid());
/* prepare argv for serving-processes */
cld_argv = xmalloc(sizeof (char *) * (argc + 2));
diff --git a/submodule.c b/submodule.c
index 613857e..fe5748d 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1135,16 +1135,11 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
struct strbuf file_name = STRBUF_INIT;
struct strbuf rel_path = STRBUF_INIT;
const char *real_work_tree = xstrdup(real_path(work_tree));
- FILE *fp;
/* Update gitfile */
strbuf_addf(&file_name, "%s/.git", work_tree);
- fp = fopen(file_name.buf, "w");
- if (!fp)
- die(_("Could not create git link %s"), file_name.buf);
- fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
- &rel_path));
- fclose(fp);
+ write_file(file_name.buf, 1, "gitdir: %s\n",
+ relative_path(git_dir, real_work_tree, &rel_path));
/* Update core.worktree setting */
strbuf_reset(&file_name);
diff --git a/transport.c b/transport.c
index ca7bb44..2df8a15 100644
--- a/transport.c
+++ b/transport.c
@@ -294,7 +294,6 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
{
struct strbuf *buf = data;
int len = buf->len;
- FILE *f;
/* when called via for_each_ref(), flags is non-zero */
if (flags && !starts_with(name, "refs/heads/") &&
@@ -303,10 +302,9 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
strbuf_addstr(buf, name);
if (safe_create_leading_directories(buf->buf) ||
- !(f = fopen(buf->buf, "w")) ||
- fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
- fclose(f))
- return error("problems writing temporary file %s", buf->buf);
+ write_file(buf->buf, 0, "%s\n", sha1_to_hex(sha1)))
+ return error("problems writing temporary file %s: %s",
+ buf->buf, strerror(errno));
strbuf_setlen(buf, len);
return 0;
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 21/27] checkout: support checking out into a new working directory
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (19 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 20/27] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:12 ` [PATCH v4 22/27] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
` (6 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
"git checkout --to" sets up a new working directory with a .git file
pointing to $GIT_DIR/repos/<id>. It then executes "git checkout" again
on the new worktree with the same arguments except "--to" is taken
out. The second checkout execution, which is not contaminated with any
info from the current repository, will actually check out and
everything that normal "git checkout" does.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-checkout.txt | 34 +++++++++++++
Documentation/git.txt | 3 +-
Documentation/gitrepository-layout.txt | 7 +++
builtin/checkout.c | 93 +++++++++++++++++++++++++++++++++-
path.c | 2 +-
t/t2025-checkout-to.sh (new +x) | 48 ++++++++++++++++++
6 files changed, 183 insertions(+), 4 deletions(-)
create mode 100755 t/t2025-checkout-to.sh
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 33ad2ad..fcf73b2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -225,6 +225,13 @@ This means that you can use `git checkout -p` to selectively discard
edits from your current working tree. See the ``Interactive Mode''
section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
+--to=<path>::
+ Check out a new branch in a separate working directory at
+ `<path>`. A new working directory is linked to the current
+ repository, sharing everything except working directory
+ specific files such as HEAD, index... See "MULTIPLE CHECKOUT
+ MODE" section for more information.
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
@@ -388,6 +395,33 @@ $ git reflog -2 HEAD # or
$ git log -g -2 HEAD
------------
+MULTIPLE CHECKOUT MODE
+-------------------------------
+Normally a working directory is attached to repository. When "git
+checkout --to" is used, a new working directory is attached to the
+current repository. This new working directory is called "linked
+checkout" as compared to the "main checkout" prepared by "git init" or
+"git clone". A repository has one main checkout and zero or more
+linked checkouts.
+
+All checkouts share the same repository. Linked checkouts see the
+repository a bit different from the main checkout. When the checkout
+"new" reads the path $GIT_DIR/HEAD for example, the actual path
+returned could be $GIT_DIR/repos/new/HEAD. This ensures checkouts
+won't step on each other.
+
+Each linked checkout has a private space in $GIT_DIR/repos, usually
+named after the base name of the working directory with a number added
+to make it unique. The linked checkout's $GIT_DIR points to this
+private space while $GIT_COMMON_DIR points to the main checkout's
+$GIT_DIR. These settings are done by "git checkout --to".
+
+Because in this mode $GIT_DIR becomes a lightweight virtual file
+system where a path could be rewritten to some place else, accessing
+$GIT_DIR from scripts should use `git rev-parse --git-path` to resolve
+a path instead of using it directly unless the path is known to be
+private to the working directory.
+
EXAMPLES
--------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b094b1f..bdb9b0f 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -777,7 +777,8 @@ Git so take care if using Cogito etc.
If this variable is set to a path, non-worktree files that are
normally in $GIT_DIR will be taken from this path
instead. Worktree-specific files such as HEAD or index are
- taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for
+ taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and
+ the section 'MULTIPLE CHECKOUT MODE' in linkgit:checkout[1]
details. This variable has lower precedence than other path
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index f72ce75..418e5c8 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -245,6 +245,13 @@ modules::
directory is ignored $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/modules" will be used instead.
+repos::
+ Contains worktree specific information of linked
+ checkouts. Each subdirectory contains the worktree-related
+ part of a linked checkout. This directory is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/repos" will be
+ used instead.
+
SEE ALSO
--------
linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0570e41..fa7b54a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -48,6 +48,10 @@ struct checkout_opts {
const char *prefix;
struct pathspec pathspec;
struct tree *source_tree;
+
+ const char *new_worktree;
+ const char **saved_argv;
+ int new_worktree_mode;
};
static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -250,6 +254,9 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("Cannot update paths and switch to branch '%s' at the same time."),
opts->new_branch);
+ if (opts->new_worktree)
+ die(_("'%s' cannot be used with updating paths"), "--to");
+
if (opts->patch_mode)
return run_add_interactive(revision, "--patch=checkout",
&opts->pathspec);
@@ -486,7 +493,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.dir->flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(topts.dir);
}
- tree = parse_tree_indirect(old->commit ?
+ tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?
old->commit->object.sha1 :
EMPTY_TREE_SHA1_BIN);
init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -796,7 +803,8 @@ static int switch_branches(const struct checkout_opts *opts,
return ret;
}
- if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
+ if (!opts->quiet && !old.path && old.commit &&
+ new->commit != old.commit && !opts->new_worktree_mode)
orphaned_commit_warning(old.commit, new->commit);
update_refs_for_switch(opts, &old, new);
@@ -806,6 +814,74 @@ static int switch_branches(const struct checkout_opts *opts,
return ret || writeout_error;
}
+static int prepare_linked_checkout(const struct checkout_opts *opts,
+ struct branch_info *new)
+{
+ struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ const char *path = opts->new_worktree, *name;
+ struct stat st;
+ struct child_process cp;
+ int counter = 0, len;
+
+ if (!new->commit)
+ die(_("no branch specified"));
+
+ len = strlen(path);
+ while (len && is_dir_sep(path[len - 1]))
+ len--;
+
+ for (name = path + len - 1; name > path; name--)
+ if (is_dir_sep(*name)) {
+ name++;
+ break;
+ }
+ strbuf_addstr(&sb_repo,
+ git_path("repos/%.*s", (int)(path + len - name), name));
+ len = sb_repo.len;
+ if (safe_create_leading_directories_const(sb_repo.buf))
+ die_errno(_("could not create leading directories of '%s'"),
+ sb_repo.buf);
+ while (!stat(sb_repo.buf, &st)) {
+ counter++;
+ strbuf_setlen(&sb_repo, len);
+ strbuf_addf(&sb_repo, "%d", counter);
+ }
+ name = strrchr(sb_repo.buf, '/') + 1;
+ if (mkdir(sb_repo.buf, 0777))
+ die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+
+ strbuf_addf(&sb_git, "%s/.git", path);
+ if (safe_create_leading_directories_const(sb_git.buf))
+ die_errno(_("could not create leading directories of '%s'"),
+ sb_git.buf);
+
+ write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
+ real_path(get_git_common_dir()), name);
+ /*
+ * This is to keep resolve_ref() happy. We need a valid HEAD
+ * or is_git_directory() will reject the directory. Any valid
+ * value would do because this value will be ignored and
+ * replaced at the next (real) checkout.
+ */
+ strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+ write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
+ write_file(sb.buf, 1, "../..\n");
+
+ if (!opts->quiet)
+ fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
+
+ setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
+ setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
+ setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
+ memset(&cp, 0, sizeof(cp));
+ cp.git_cmd = 1;
+ cp.argv = opts->saved_argv;
+ return run_command(&cp);
+}
+
static int git_checkout_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.ignoresubmodules")) {
@@ -1067,6 +1143,9 @@ static int checkout_branch(struct checkout_opts *opts,
die(_("Cannot switch branch to a non-commit '%s'"),
new->name);
+ if (opts->new_worktree)
+ return prepare_linked_checkout(opts, new);
+
if (!new->commit && opts->new_branch) {
unsigned char rev[20];
int flag;
@@ -1109,6 +1188,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("do not limit pathspecs to sparse entries only")),
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
N_("second guess 'git checkout no-such-branch'")),
+ OPT_STRING(0, "to", &opts.new_worktree, N_("path"),
+ N_("check a branch out in a separate working directory")),
OPT_END(),
};
@@ -1117,6 +1198,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.overwrite_ignore = 1;
opts.prefix = prefix;
+ opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
+ memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
+
gitmodules_config();
git_config(git_checkout_config, &opts);
@@ -1125,6 +1209,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ /* recursive execution from checkout_new_worktree() */
+ opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
+ if (opts.new_worktree_mode)
+ opts.new_worktree = NULL;
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --git a/path.c b/path.c
index 6129026..47383ff 100644
--- a/path.c
+++ b/path.c
@@ -94,7 +94,7 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
{
const char *common_dir_list[] = {
"branches", "hooks", "info", "logs", "lost-found", "modules",
- "objects", "refs", "remotes", "rr-cache", "svn",
+ "objects", "refs", "remotes", "repos", "rr-cache", "svn",
NULL
};
const char *common_top_file_list[] = {
diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh
new file mode 100755
index 0000000..5ec49e2
--- /dev/null
+++ b/t/t2025-checkout-to.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test git checkout --to'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+test_expect_success 'checkout --to not updating paths' '
+ test_must_fail git checkout --to -- init.t
+'
+
+test_expect_success 'checkout --to a new worktree' '
+ git checkout --to here master &&
+ (
+ cd here &&
+ test_cmp ../init.t init.t &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/master >expect &&
+ test_cmp expect actual &&
+ git fsck
+ )
+'
+
+test_expect_success 'checkout --to from a linked checkout' '
+ (
+ cd here &&
+ git checkout --to nested-here master
+ cd nested-here &&
+ git fsck
+ )
+'
+
+test_expect_success 'checkout --to a new worktree creating new branch' '
+ git checkout --to there -b newmaster master &&
+ (
+ cd there &&
+ test_cmp ../init.t init.t &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/newmaster >expect &&
+ test_cmp expect actual &&
+ git fsck
+ )
+'
+
+test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 22/27] checkout: clean up half-prepared directories in --to mode
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (20 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 21/27] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-06 10:06 ` Eric Sunshine
2014-03-01 12:12 ` [PATCH v4 23/27] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
` (5 subsequent siblings)
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index fa7b54a..28f9ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -20,6 +20,7 @@
#include "resolve-undo.h"
#include "submodule.h"
#include "argv-array.h"
+#include "sigchain.h"
static const char * const checkout_usage[] = {
N_("git checkout [options] <branch>"),
@@ -814,6 +815,35 @@ static int switch_branches(const struct checkout_opts *opts,
return ret || writeout_error;
}
+static const char *junk_work_tree;
+static const char *junk_git_dir;
+static int is_junk;
+static pid_t junk_pid;
+
+static void remove_junk(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (!is_junk || getpid() != junk_pid)
+ return;
+ if (junk_git_dir) {
+ strbuf_addstr(&sb, junk_git_dir);
+ remove_dir_recursively(&sb, 0);
+ strbuf_reset(&sb);
+ }
+ if (junk_work_tree) {
+ strbuf_addstr(&sb, junk_work_tree);
+ remove_dir_recursively(&sb, 0);
+ strbuf_reset(&sb);
+ }
+}
+
+static void remove_junk_on_signal(int signo)
+{
+ remove_junk();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
static int prepare_linked_checkout(const struct checkout_opts *opts,
struct branch_info *new)
{
@@ -822,7 +852,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
const char *path = opts->new_worktree, *name;
struct stat st;
struct child_process cp;
- int counter = 0, len;
+ int counter = 0, len, ret;
if (!new->commit)
die(_("no branch specified"));
@@ -848,13 +878,21 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
strbuf_addf(&sb_repo, "%d", counter);
}
name = strrchr(sb_repo.buf, '/') + 1;
+
+ junk_pid = getpid();
+ atexit(remove_junk);
+ sigchain_push_common(remove_junk_on_signal);
+
if (mkdir(sb_repo.buf, 0777))
die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+ junk_git_dir = sb_repo.buf;
+ is_junk = 1;
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
+ junk_work_tree = path;
write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
real_path(get_git_common_dir()), name);
@@ -879,7 +917,14 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
memset(&cp, 0, sizeof(cp));
cp.git_cmd = 1;
cp.argv = opts->saved_argv;
- return run_command(&cp);
+ ret = run_command(&cp);
+ if (!ret)
+ is_junk = 0;
+ strbuf_release(&sb);
+ strbuf_release(&sb_repo);
+ strbuf_release(&sb_git);
+ return ret;
+
}
static int git_checkout_config(const char *var, const char *value, void *cb)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 22/27] checkout: clean up half-prepared directories in --to mode
2014-03-01 12:12 ` [PATCH v4 22/27] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
@ 2014-03-06 10:06 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-06 10:06 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:12 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> builtin/checkout.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 47 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index fa7b54a..28f9ac1 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -20,6 +20,7 @@
> #include "resolve-undo.h"
> #include "submodule.h"
> #include "argv-array.h"
> +#include "sigchain.h"
>
> static const char * const checkout_usage[] = {
> N_("git checkout [options] <branch>"),
> @@ -814,6 +815,35 @@ static int switch_branches(const struct checkout_opts *opts,
> return ret || writeout_error;
> }
>
> +static const char *junk_work_tree;
> +static const char *junk_git_dir;
> +static int is_junk;
> +static pid_t junk_pid;
> +
> +static void remove_junk(void)
> +{
> + struct strbuf sb = STRBUF_INIT;
> + if (!is_junk || getpid() != junk_pid)
> + return;
> + if (junk_git_dir) {
> + strbuf_addstr(&sb, junk_git_dir);
> + remove_dir_recursively(&sb, 0);
> + strbuf_reset(&sb);
> + }
> + if (junk_work_tree) {
> + strbuf_addstr(&sb, junk_work_tree);
> + remove_dir_recursively(&sb, 0);
> + strbuf_reset(&sb);
> + }
strbuf_release(&sb);
> +}
> +
> +static void remove_junk_on_signal(int signo)
> +{
> + remove_junk();
> + sigchain_pop(signo);
> + raise(signo);
> +}
> +
> static int prepare_linked_checkout(const struct checkout_opts *opts,
> struct branch_info *new)
> {
> @@ -822,7 +852,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
> const char *path = opts->new_worktree, *name;
> struct stat st;
> struct child_process cp;
> - int counter = 0, len;
> + int counter = 0, len, ret;
>
> if (!new->commit)
> die(_("no branch specified"));
> @@ -848,13 +878,21 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
> strbuf_addf(&sb_repo, "%d", counter);
> }
> name = strrchr(sb_repo.buf, '/') + 1;
> +
> + junk_pid = getpid();
> + atexit(remove_junk);
> + sigchain_push_common(remove_junk_on_signal);
> +
> if (mkdir(sb_repo.buf, 0777))
> die_errno(_("could not create directory of '%s'"), sb_repo.buf);
> + junk_git_dir = sb_repo.buf;
> + is_junk = 1;
>
> strbuf_addf(&sb_git, "%s/.git", path);
> if (safe_create_leading_directories_const(sb_git.buf))
> die_errno(_("could not create leading directories of '%s'"),
> sb_git.buf);
> + junk_work_tree = path;
>
> write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
> real_path(get_git_common_dir()), name);
> @@ -879,7 +917,14 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
> memset(&cp, 0, sizeof(cp));
> cp.git_cmd = 1;
> cp.argv = opts->saved_argv;
> - return run_command(&cp);
> + ret = run_command(&cp);
> + if (!ret)
> + is_junk = 0;
> + strbuf_release(&sb);
> + strbuf_release(&sb_repo);
> + strbuf_release(&sb_git);
> + return ret;
> +
> }
>
> static int git_checkout_config(const char *var, const char *value, void *cb)
> --
> 1.9.0.40.gaa8c3ea
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 23/27] checkout: detach if the branch is already checked out elsewhere
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (21 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 22/27] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:12 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:13 ` [PATCH v4 24/27] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
` (4 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The normal rule is anything outside refs/heads/ is detached. This
increases strictness of the rule a bit more: if the branch is checked
out (either in $GIT_COMMON_DIR/HEAD or any $GIT_DIR/repos/.../HEAD)
then it's detached as well.
A hint is given so the user knows where to go and do something there
if they still want to checkout undetached here.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++
t/t2025-checkout-to.sh | 15 ++++++++--
2 files changed, 92 insertions(+), 3 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 28f9ac1..1675808 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -433,6 +433,11 @@ struct branch_info {
const char *name; /* The short name used */
const char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
+ /*
+ * if not null the branch is detached because it's already
+ * checked out in this checkout
+ */
+ char *checkout;
};
static void setup_branch_path(struct branch_info *branch)
@@ -640,6 +645,11 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (old->path && advice_detached_head)
detach_advice(new->name);
describe_detached_head(_("HEAD is now at"), new->commit);
+ if (new->checkout && !*new->checkout)
+ fprintf(stderr, _("hint: the main checkout is holding this branch\n"));
+ else if (new->checkout)
+ fprintf(stderr, _("hint: the linked checkout %s is holding this branch\n"),
+ new->checkout);
}
} else if (new->path) { /* Switch branches. */
create_symref("HEAD", new->path, msg.buf);
@@ -982,6 +992,73 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
return NULL;
}
+static int check_linked_checkout(struct branch_info *new,
+ const char *name, const char *path)
+{
+ struct strbuf sb = STRBUF_INIT;
+ char *start, *end;
+ if (strbuf_read_file(&sb, path, 0) < 0)
+ return 0;
+ if (!starts_with(sb.buf, "ref:")) {
+ strbuf_release(&sb);
+ return 0;
+ }
+
+ start = sb.buf + 4;
+ while (isspace(*start))
+ start++;
+ end = start;
+ while (*end && !isspace(*end))
+ end++;
+ if (!strncmp(start, new->path, end - start) &&
+ new->path[end - start] == '\0') {
+ strbuf_release(&sb);
+ new->path = NULL; /* detach */
+ new->checkout = xstrdup(name); /* reason */
+ return 1;
+ }
+ strbuf_release(&sb);
+ return 0;
+}
+
+static void check_linked_checkouts(struct branch_info *new)
+{
+ struct strbuf path = STRBUF_INIT;
+ DIR *dir;
+ struct dirent *d;
+
+ strbuf_addf(&path, "%s/repos", get_git_common_dir());
+ if ((dir = opendir(path.buf)) == NULL) {
+ strbuf_release(&path);
+ return;
+ }
+
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+ /*
+ * $GIT_COMMON_DIR/HEAD is practically outside
+ * $GIT_DIR so resolve_ref_unsafe() won't work (it
+ * uses git_path). Parse the ref ourselves.
+ */
+ if (check_linked_checkout(new, "", path.buf)) {
+ strbuf_release(&path);
+ closedir(dir);
+ return;
+ }
+
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/repos/%s/HEAD",
+ get_git_common_dir(), d->d_name);
+ if (check_linked_checkout(new, d->d_name, path.buf))
+ break;
+ }
+ strbuf_release(&path);
+ closedir(dir);
+}
+
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new,
@@ -1109,6 +1186,9 @@ static int parse_branchname_arg(int argc, const char **argv,
else
new->path = NULL; /* not an existing branch */
+ if (new->path)
+ check_linked_checkouts(new);
+
new->commit = lookup_commit_reference_gently(rev, 1);
if (!new->commit) {
/* not a commit */
diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh
index 5ec49e2..2d35a9b 100755
--- a/t/t2025-checkout-to.sh
+++ b/t/t2025-checkout-to.sh
@@ -13,13 +13,14 @@ test_expect_success 'checkout --to not updating paths' '
'
test_expect_success 'checkout --to a new worktree' '
+ git rev-parse HEAD >expect &&
git checkout --to here master &&
(
cd here &&
test_cmp ../init.t init.t &&
- git symbolic-ref HEAD >actual &&
- echo refs/heads/master >expect &&
- test_cmp expect actual &&
+ test_must_fail git symbolic-ref HEAD &&
+ git rev-parse HEAD >actual &&
+ test_cmp ../expect actual &&
git fsck
)
'
@@ -45,4 +46,12 @@ test_expect_success 'checkout --to a new worktree creating new branch' '
)
'
+test_expect_success 'detach if the same branch is already checked out' '
+ (
+ cd here &&
+ git checkout newmaster &&
+ test_must_fail git symbolic-ref HEAD
+ )
+'
+
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 24/27] prune: strategies for linked checkouts
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (22 preceding siblings ...)
2014-03-01 12:12 ` [PATCH v4 23/27] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:13 ` Nguyễn Thái Ngọc Duy
2014-03-01 17:12 ` Torsten Bögershausen
2014-03-05 20:07 ` Junio C Hamano
2014-03-01 12:13 ` [PATCH v4 25/27] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
` (3 subsequent siblings)
27 siblings, 2 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
(alias R=$GIT_COMMON_DIR/repos/<id>)
- linked checkouts are supposed to keep its location in $R/gitdir up
to date. The use case is auto fixup after a manual checkout move.
- linked checkouts are supposed to update mtime of $R/gitdir. If
$R/gitdir's mtime is older than a limit, and it points to nowhere,
repos/<id> is to be pruned.
- "git checkout --to" is supposed to create $R/locked if the new repo
is on a different partition than the shared one. The main use case
is when the checkout is on a portable device and may not be
available at prune time.
If $R/locked exists, repos/<id> is not supposed to be pruned. If
$R/locked exists and $R/gitdir's mtime is older than a really long
limit, warn about old unused repo.
- "git checkout --to" is supposed to make a hard link named $R/link
pointing to the .git file on supported file systems to help detect
the user manually deleting the checkout. If $R/link exists and its
link count is greated than 1, the repo is kept.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-prune.txt | 3 ++
Documentation/gitrepository-layout.txt | 19 +++++++++
builtin/checkout.c | 36 ++++++++++++++++-
builtin/prune.c | 74 ++++++++++++++++++++++++++++++++++
compat/mingw.h | 1 +
git-compat-util.h | 4 ++
setup.c | 13 ++++++
7 files changed, 149 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 058ac0d..7babf11 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -48,6 +48,9 @@ OPTIONS
--expire <time>::
Only expire loose objects older than <time>.
+--repos::
+ Prune directories in $GIT_DIR/repos.
+
<head>...::
In addition to objects
reachable from any of our references, keep objects
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 418e5c8..2dc6901 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -252,6 +252,25 @@ repos::
$GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/repos" will be
used instead.
+repos/<id>/gitdir::
+ A text file containing the absolute path back to the .git file
+ that points to here. This is used to check if the linked
+ repository has been manually removed and there is no need to
+ keep this directory any more. mtime of this file should be
+ updated every time the linked repository is accessed.
+
+repos/<id>/locked::
+ If this file exists, the linked repository may be on a
+ portable device and not available. It does not mean that the
+ linked repository is gone and `repos/<id>` could be
+ removed. The file's content contains a reason string on why
+ the repository is locked.
+
+repos/<id>/link::
+ If this file exists, it is a hard link to the linked .git
+ file. It is used to detect if the linked repository is
+ manually removed.
+
SEE ALSO
--------
linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1675808..1fc85d3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -854,6 +854,17 @@ static void remove_junk_on_signal(int signo)
raise(signo);
}
+static dev_t get_device_or_die(const char *path)
+{
+ struct stat buf;
+ if (stat(path, &buf))
+ die_errno("failed to stat '%s'", path);
+ /* Ah Windows! Make different drives different "partitions" */
+ if (is_windows())
+ buf.st_dev = toupper(real_path(path)[0]);
+ return buf.st_dev;
+}
+
static int prepare_linked_checkout(const struct checkout_opts *opts,
struct branch_info *new)
{
@@ -862,7 +873,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
const char *path = opts->new_worktree, *name;
struct stat st;
struct child_process cp;
- int counter = 0, len, ret;
+ int counter = 0, len, keep_locked = 0, ret;
if (!new->commit)
die(_("no branch specified"));
@@ -898,12 +909,18 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
junk_git_dir = sb_repo.buf;
is_junk = 1;
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ write_file(sb.buf, 1, "initializing\n");
+
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
junk_work_tree = path;
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+ write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
real_path(get_git_common_dir()), name);
/*
@@ -912,12 +929,24 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
* value would do because this value will be ignored and
* replaced at the next (real) checkout.
*/
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, 1, "../..\n");
+ if (get_device_or_die(path) != get_device_or_die(get_git_dir())) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ write_file(sb.buf, 1, "located on a different file system\n");
+ keep_locked = 1;
+ } else {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/link", sb_repo.buf);
+ (void)link(sb_git.buf, sb.buf);
+ }
+
if (!opts->quiet)
fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
@@ -930,6 +959,11 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
ret = run_command(&cp);
if (!ret)
is_junk = 0;
+ if (!keep_locked) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ unlink_or_warn(sb.buf);
+ }
strbuf_release(&sb);
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
diff --git a/builtin/prune.c b/builtin/prune.c
index de43b26..733cb3b 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -112,6 +112,70 @@ static void prune_object_dir(const char *path)
}
}
+static const char *prune_repo_dir(const char *id, struct stat *st)
+{
+ char *path;
+ int fd, len;
+ if (file_exists(git_path("repos/%s/locked", id)))
+ return NULL;
+ if (stat(git_path("repos/%s/gitdir", id), st)) {
+ st->st_mtime = expire;
+ return _("gitdir does not exist");
+ }
+ fd = open(git_path("repos/%s/gitdir", id), O_RDONLY);
+ len = st->st_size;
+ path = xmalloc(len + 1);
+ read_in_full(fd, path, len);
+ close(fd);
+ while (path[len - 1] == '\n' || path[len - 1] == '\r')
+ len--;
+ path[len] = '\0';
+ if (!file_exists(path)) {
+ struct stat st_link;
+ free(path);
+ /*
+ * the repo is moved manually and has not been
+ * accessed since?
+ */
+ if (!stat(git_path("repos/%s/link", id), &st_link) &&
+ st_link.st_nlink > 1)
+ return NULL;
+ return _("gitdir points to non-existing file");
+ }
+ free(path);
+ return NULL;
+}
+
+static void prune_repos_dir(void)
+{
+ const char *reason;
+ DIR *dir = opendir(git_path("repos"));
+ struct dirent *d;
+ int removed = 0;
+ struct stat st;
+ if (!dir)
+ return;
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ if ((reason = prune_repo_dir(d->d_name, &st)) != NULL &&
+ st.st_mtime <= expire) {
+ struct strbuf sb = STRBUF_INIT;
+ if (show_only || verbose)
+ printf(_("Removing repos/%s: %s\n"), d->d_name, reason);
+ if (show_only)
+ continue;
+ strbuf_addstr(&sb, git_path("repos/%s", d->d_name));
+ remove_dir_recursively(&sb, 0);
+ strbuf_release(&sb);
+ removed = 1;
+ }
+ }
+ closedir(dir);
+ if (removed)
+ rmdir(git_path("repos"));
+}
+
/*
* Write errors (particularly out of space) can result in
* failed temporary packs (and more rarely indexes and other
@@ -138,10 +202,12 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct progress *progress = NULL;
+ int prune_repos = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_BOOL(0, "repos", &prune_repos, N_("prune .git/repos/")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
OPT_END()
@@ -154,6 +220,14 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
init_revisions(&revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+
+ if (prune_repos) {
+ if (argc)
+ die(_("--repos does not take extra arguments"));
+ prune_repos_dir();
+ return 0;
+ }
+
while (argc--) {
unsigned char sha1[20];
const char *name = *argv++;
diff --git a/compat/mingw.h b/compat/mingw.h
index e033e72..18323c1 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -328,6 +328,7 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
* git specific compatibility
*/
+#define is_windows() 1
#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
static inline char *mingw_find_last_dir_sep(const char *path)
diff --git a/git-compat-util.h b/git-compat-util.h
index cbd86c3..41f1b74 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -266,6 +266,10 @@ extern char *gitbasename(char *);
#define STRIP_EXTENSION ""
#endif
+#ifndef is_windows
+#define is_windows() 0
+#endif
+
#ifndef has_dos_drive_prefix
#define has_dos_drive_prefix(path) 0
#endif
diff --git a/setup.c b/setup.c
index 40ce191..5529b26 100644
--- a/setup.c
+++ b/setup.c
@@ -336,6 +336,17 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
return ret;
}
+static void update_linked_gitdir(const char *gitfile, const char *gitdir)
+{
+ struct strbuf path = STRBUF_INIT;
+ struct stat st;
+
+ strbuf_addf(&path, "%s/gitfile", gitdir);
+ if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
+ write_file(path.buf, 0, "%s\n", gitfile);
+ strbuf_release(&path);
+}
+
/*
* Try to read the location of the git directory from the .git file,
* return path to git directory if found.
@@ -384,6 +395,8 @@ const char *read_gitfile(const char *path)
if (!is_git_directory(dir))
die("Not a git repository: %s", dir);
+
+ update_linked_gitdir(path, dir);
path = real_path(dir);
free(buf);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 24/27] prune: strategies for linked checkouts
2014-03-01 12:13 ` [PATCH v4 24/27] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
@ 2014-03-01 17:12 ` Torsten Bögershausen
2014-03-02 0:01 ` Duy Nguyen
2014-03-05 20:07 ` Junio C Hamano
1 sibling, 1 reply; 169+ messages in thread
From: Torsten Bögershausen @ 2014-03-01 17:12 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano
On 2014-03-01 13.13, Nguyễn Thái Ngọc Duy wrote:
[]
>
> +static dev_t get_device_or_die(const char *path)
> +{
> + struct stat buf;
> + if (stat(path, &buf))
> + die_errno("failed to stat '%s'", path);
> + /* Ah Windows! Make different drives different "partitions" */
> + if (is_windows())
> + buf.st_dev = toupper(real_path(path)[0]);
> + return buf.st_dev;
Is this only related to Windows ?
Do we have other file systems, which return st_dev == 0 ?
Should we check that path[0] != '/', or better !is_dir_sep(path[0]) ?
Do we need has_dos_drive_prefix() ?
As a first suggestion, would this be better:
> + if (!buf.st_dev)
> + buf.st_dev = toupper(real_path(path)[0]);
(End of loose thinking)
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 24/27] prune: strategies for linked checkouts
2014-03-01 17:12 ` Torsten Bögershausen
@ 2014-03-02 0:01 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-02 0:01 UTC (permalink / raw)
To: Torsten Bögershausen; +Cc: Git Mailing List, Junio C Hamano
On Sun, Mar 2, 2014 at 12:12 AM, Torsten Bögershausen <tboegi@web.de> wrote:
> On 2014-03-01 13.13, Nguyễn Thái Ngọc Duy wrote:
> []
>>
>> +static dev_t get_device_or_die(const char *path)
>> +{
>> + struct stat buf;
>> + if (stat(path, &buf))
>> + die_errno("failed to stat '%s'", path);
>> + /* Ah Windows! Make different drives different "partitions" */
>> + if (is_windows())
>> + buf.st_dev = toupper(real_path(path)[0]);
>> + return buf.st_dev;
>
> Is this only related to Windows ?
Yes. At least the treatment is Windows specific. If st_dev == 0 in
other cases, then we have to deal with them case-by-case.
> Do we have other file systems, which return st_dev == 0 ?
> Should we check that path[0] != '/', or better !is_dir_sep(path[0]) ?
> Do we need has_dos_drive_prefix() ?
real_path() returns an absolute path, so we're guaranteed its first
character is the drive letter, right? (I tried to confirm this by
reading read_path_internal, but it's a bit complex, and I don't have
Windows machine to quickly test it out)
>
> As a first suggestion, would this be better:
>
>> + if (!buf.st_dev)
>> + buf.st_dev = toupper(real_path(path)[0]);
>
> (End of loose thinking)
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 24/27] prune: strategies for linked checkouts
2014-03-01 12:13 ` [PATCH v4 24/27] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
2014-03-01 17:12 ` Torsten Bögershausen
@ 2014-03-05 20:07 ` Junio C Hamano
1 sibling, 0 replies; 169+ messages in thread
From: Junio C Hamano @ 2014-03-05 20:07 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> + if (get_device_or_die(path) != get_device_or_die(get_git_dir())) {
> + strbuf_reset(&sb);
> + strbuf_addf(&sb, "%s/locked", sb_repo.buf);
> + write_file(sb.buf, 1, "located on a different file system\n");
> + keep_locked = 1;
> + } else {
> + strbuf_reset(&sb);
> + strbuf_addf(&sb, "%s/link", sb_repo.buf);
> + (void)link(sb_git.buf, sb.buf);
> + }
Just in case you did not realize, casting the return away with
(void) will not squelch this out of the compiler:
builtin/checkout.c: In function 'prepare_linked_checkout':
builtin/checkout.c:947:3: error: ignoring return value of 'link', declared with attribute warn_unused_result [-Werror=unused-result]
It still feels fishy to see "we attempt to link but we do not care
if it works or not" to me, with or without the "unused result"
issue.
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 25/27] gc: style change -- no SP before closing bracket
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (23 preceding siblings ...)
2014-03-01 12:13 ` [PATCH v4 24/27] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:13 ` Nguyễn Thái Ngọc Duy
2014-03-01 12:13 ` [PATCH v4 26/27] gc: support prune --repos Nguyễn Thái Ngọc Duy
` (2 subsequent siblings)
27 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/gc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index c19545d..39d9b27 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -260,7 +260,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("suppress progress reporting")),
{ OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
N_("prune unreferenced objects"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
+ PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire},
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")),
OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")),
@@ -273,7 +273,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
- argv_array_pushl(&prune, "prune", "--expire", NULL );
+ argv_array_pushl(&prune, "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
git_config(gc_config, NULL);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v4 26/27] gc: support prune --repos
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (24 preceding siblings ...)
2014-03-01 12:13 ` [PATCH v4 25/27] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:13 ` Nguyễn Thái Ngọc Duy
2014-03-07 6:40 ` Eric Sunshine
2014-03-01 12:13 ` [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 6 ++++++
builtin/gc.c | 17 +++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 313d4b3..438b213 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1183,6 +1183,12 @@ gc.pruneexpire::
"now" may be used to disable this grace period and always prune
unreachable objects immediately.
+gc.prunereposexpire::
+ When 'git gc' is run, it will call 'prune --repos --expire 3.months.ago'.
+ Override the grace period with this config variable. The value
+ "now" may be used to disable this grace period and always prune
+ $GIT_DIR/repos immediately.
+
gc.reflogexpire::
gc.<pattern>.reflogexpire::
'git reflog expire' removes reflog entries older than
diff --git a/builtin/gc.c b/builtin/gc.c
index 39d9b27..85c3c0c 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -30,11 +30,13 @@ static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static const char *prune_expire = "2.weeks.ago";
+static const char *prune_repos_expire = "3.months.ago";
static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
static struct argv_array reflog = ARGV_ARRAY_INIT;
static struct argv_array repack = ARGV_ARRAY_INIT;
static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array prune_repos = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
static char *pidfile;
@@ -81,6 +83,14 @@ static int gc_config(const char *var, const char *value, void *cb)
}
return git_config_string(&prune_expire, var, value);
}
+ if (!strcmp(var, "gc.prunereposexpire")) {
+ if (value && strcmp(value, "now")) {
+ unsigned long now = approxidate("now");
+ if (approxidate(value) >= now)
+ return error(_("Invalid %s: '%s'"), var, value);
+ }
+ return git_config_string(&prune_repos_expire, var, value);
+ }
return git_default_config(var, value, cb);
}
@@ -274,6 +284,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
argv_array_pushl(&prune, "prune", "--expire", NULL);
+ argv_array_pushl(&prune_repos, "prune", "--repos", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
git_config(gc_config, NULL);
@@ -334,6 +345,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
return error(FAILED_RUN, prune.argv[0]);
}
+ if (prune_repos_expire) {
+ argv_array_push(&prune_repos, prune_repos_expire);
+ if (run_command_v_opt(prune_repos.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, prune_repos.argv[0]);
+ }
+
if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
return error(FAILED_RUN, rerere.argv[0]);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 26/27] gc: support prune --repos
2014-03-01 12:13 ` [PATCH v4 26/27] gc: support prune --repos Nguyễn Thái Ngọc Duy
@ 2014-03-07 6:40 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-07 6:40 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:13 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> Documentation/config.txt | 6 ++++++
> builtin/gc.c | 17 +++++++++++++++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 313d4b3..438b213 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -1183,6 +1183,12 @@ gc.pruneexpire::
> "now" may be used to disable this grace period and always prune
> unreachable objects immediately.
>
> +gc.prunereposexpire::
> + When 'git gc' is run, it will call 'prune --repos --expire 3.months.ago'.
> + Override the grace period with this config variable. The value
> + "now" may be used to disable this grace period and always prune
Extra space between "this" and "grace". However, "the grace period"
sounds a bit better.
> + $GIT_DIR/repos immediately.
> +
> gc.reflogexpire::
> gc.<pattern>.reflogexpire::
> 'git reflog expire' removes reflog entries older than
> diff --git a/builtin/gc.c b/builtin/gc.c
> index 39d9b27..85c3c0c 100644
> --- a/builtin/gc.c
> +++ b/builtin/gc.c
> @@ -30,11 +30,13 @@ static int aggressive_window = 250;
> static int gc_auto_threshold = 6700;
> static int gc_auto_pack_limit = 50;
> static const char *prune_expire = "2.weeks.ago";
> +static const char *prune_repos_expire = "3.months.ago";
>
> static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
> static struct argv_array reflog = ARGV_ARRAY_INIT;
> static struct argv_array repack = ARGV_ARRAY_INIT;
> static struct argv_array prune = ARGV_ARRAY_INIT;
> +static struct argv_array prune_repos = ARGV_ARRAY_INIT;
> static struct argv_array rerere = ARGV_ARRAY_INIT;
>
> static char *pidfile;
> @@ -81,6 +83,14 @@ static int gc_config(const char *var, const char *value, void *cb)
> }
> return git_config_string(&prune_expire, var, value);
> }
> + if (!strcmp(var, "gc.prunereposexpire")) {
> + if (value && strcmp(value, "now")) {
> + unsigned long now = approxidate("now");
> + if (approxidate(value) >= now)
> + return error(_("Invalid %s: '%s'"), var, value);
> + }
> + return git_config_string(&prune_repos_expire, var, value);
> + }
> return git_default_config(var, value, cb);
> }
>
> @@ -274,6 +284,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
> argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
> argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
> argv_array_pushl(&prune, "prune", "--expire", NULL);
> + argv_array_pushl(&prune_repos, "prune", "--repos", "--expire", NULL);
> argv_array_pushl(&rerere, "rerere", "gc", NULL);
>
> git_config(gc_config, NULL);
> @@ -334,6 +345,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
> return error(FAILED_RUN, prune.argv[0]);
> }
>
> + if (prune_repos_expire) {
> + argv_array_push(&prune_repos, prune_repos_expire);
> + if (run_command_v_opt(prune_repos.argv, RUN_GIT_CMD))
> + return error(FAILED_RUN, prune_repos.argv[0]);
> + }
> +
> if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
> return error(FAILED_RUN, rerere.argv[0]);
>
> --
> 1.9.0.40.gaa8c3ea
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (25 preceding siblings ...)
2014-03-01 12:13 ` [PATCH v4 26/27] gc: support prune --repos Nguyễn Thái Ngọc Duy
@ 2014-03-01 12:13 ` Nguyễn Thái Ngọc Duy
2014-03-05 4:25 ` Eric Sunshine
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
27 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-01 12:13 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In linked checkouts, borrowed parts like config is taken from
$GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as
garbage.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/count-objects.c | 37 ++++++++++++++++++++++++++++++++++++-
path.c | 4 ++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index a7f70cb..725cd5f 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -78,6 +78,39 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
}
}
+static void report_linked_checkout_garbage(void)
+{
+ /*
+ * must be more or less in sync with * path.c:update_common_dir().
+ *
+ * "logs" is let slip because logs/HEAD is in $GIT_DIR but the
+ * remaining in $GIT_COMMON_DIR. Probably not worth traversing
+ * the entire "logs" directory for that.
+ *
+ * The same "gc.pid" for because it's a temporary file.
+ */
+ const char *list[] = {
+ "branches", "hooks", "info", "lost-found", "modules",
+ "objects", "refs", "remotes", "rr-cache", "svn",
+ "config", "packed-refs", "shallow", NULL
+ };
+ struct strbuf sb = STRBUF_INIT;
+ const char **p;
+ int len;
+
+ if (!file_exists(git_path("commondir")))
+ return;
+ strbuf_addf(&sb, "%s/", get_git_dir());
+ len = sb.len;
+ for (p = list; *p; p++) {
+ strbuf_setlen(&sb, len);
+ strbuf_addstr(&sb, *p);
+ if (file_exists(sb.buf))
+ report_garbage("unused in linked checkout", sb.buf);
+ }
+ strbuf_release(&sb);
+}
+
static char const * const count_objects_usage[] = {
N_("git count-objects [-v] [-H | --human-readable]"),
NULL
@@ -102,8 +135,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
/* we do not take arguments other than flags for now */
if (argc)
usage_with_options(count_objects_usage, opts);
- if (verbose)
+ if (verbose) {
report_garbage = real_report_garbage;
+ report_linked_checkout_garbage();
+ }
memcpy(path, objdir, len);
if (len && objdir[len-1] != '/')
path[len++] = '/';
diff --git a/path.c b/path.c
index 47383ff..2e6035d 100644
--- a/path.c
+++ b/path.c
@@ -92,6 +92,10 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
static void update_common_dir(struct strbuf *buf, int git_dir_len)
{
+ /*
+ * Remember to report_linked_checkout_garbage()
+ * builtin/count-objects.c
+ */
const char *common_dir_list[] = {
"branches", "hooks", "info", "logs", "lost-found", "modules",
"objects", "refs", "remotes", "repos", "rr-cache", "svn",
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-01 12:13 ` [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
@ 2014-03-05 4:25 ` Eric Sunshine
2014-03-05 12:08 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Eric Sunshine @ 2014-03-05 4:25 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Sat, Mar 1, 2014 at 7:13 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> In linked checkouts, borrowed parts like config is taken from
> $GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as
> garbage.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> builtin/count-objects.c | 37 ++++++++++++++++++++++++++++++++++++-
> path.c | 4 ++++
> 2 files changed, 40 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/count-objects.c b/builtin/count-objects.c
> index a7f70cb..725cd5f 100644
> --- a/builtin/count-objects.c
> +++ b/builtin/count-objects.c
> @@ -78,6 +78,39 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
> }
> }
>
> +static void report_linked_checkout_garbage(void)
> +{
> + /*
> + * must be more or less in sync with * path.c:update_common_dir().
> + *
> + * "logs" is let slip because logs/HEAD is in $GIT_DIR but the
> + * remaining in $GIT_COMMON_DIR. Probably not worth traversing
> + * the entire "logs" directory for that.
> + *
> + * The same "gc.pid" for because it's a temporary file.
> + */
> + const char *list[] = {
> + "branches", "hooks", "info", "lost-found", "modules",
> + "objects", "refs", "remotes", "rr-cache", "svn",
> + "config", "packed-refs", "shallow", NULL
> + };
> + struct strbuf sb = STRBUF_INIT;
> + const char **p;
> + int len;
> +
> + if (!file_exists(git_path("commondir")))
> + return;
> + strbuf_addf(&sb, "%s/", get_git_dir());
> + len = sb.len;
> + for (p = list; *p; p++) {
> + strbuf_setlen(&sb, len);
> + strbuf_addstr(&sb, *p);
> + if (file_exists(sb.buf))
> + report_garbage("unused in linked checkout", sb.buf);
> + }
> + strbuf_release(&sb);
> +}
> +
> static char const * const count_objects_usage[] = {
> N_("git count-objects [-v] [-H | --human-readable]"),
> NULL
> @@ -102,8 +135,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
> /* we do not take arguments other than flags for now */
> if (argc)
> usage_with_options(count_objects_usage, opts);
> - if (verbose)
> + if (verbose) {
> report_garbage = real_report_garbage;
> + report_linked_checkout_garbage();
> + }
> memcpy(path, objdir, len);
> if (len && objdir[len-1] != '/')
> path[len++] = '/';
> diff --git a/path.c b/path.c
> index 47383ff..2e6035d 100644
> --- a/path.c
> +++ b/path.c
> @@ -92,6 +92,10 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
>
> static void update_common_dir(struct strbuf *buf, int git_dir_len)
> {
> + /*
> + * Remember to report_linked_checkout_garbage()
> + * builtin/count-objects.c
> + */
I couldn't figure out why this comment was telling me to remember to
report "linked checkout garbage" until I realized that you omitted the
word "update" (as in "remember to update"). It might be clearer to say
something along these lines:
Keep synchronized with related list in
builtin/count-objects.c:report_linked_checkout_garbage().
Is it not possible or just too much of a hassle to maintain this list
in just one place, as in a header which is included by these two
files? The exceptions, such as 'log' and 'gc.pid', could be marked by
a special character in the entry ("!gc.pid", for example) or any such
scheme.
> const char *common_dir_list[] = {
> "branches", "hooks", "info", "logs", "lost-found", "modules",
> "objects", "refs", "remotes", "repos", "rr-cache", "svn",
> --
> 1.9.0.40.gaa8c3ea
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-05 4:25 ` Eric Sunshine
@ 2014-03-05 12:08 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-05 12:08 UTC (permalink / raw)
To: Eric Sunshine; +Cc: Git List, Junio C Hamano
On Wed, Mar 5, 2014 at 11:25 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> static void update_common_dir(struct strbuf *buf, int git_dir_len)
>> {
>> + /*
>> + * Remember to report_linked_checkout_garbage()
>> + * builtin/count-objects.c
>> + */
>
> I couldn't figure out why this comment was telling me to remember to
> report "linked checkout garbage" until I realized that you omitted the
> word "update" (as in "remember to update"). It might be clearer to say
> something along these lines:
>
> Keep synchronized with related list in
> builtin/count-objects.c:report_linked_checkout_garbage().
>
I have a tendency to accidentally important words :)
> Is it not possible or just too much of a hassle to maintain this list
> in just one place, as in a header which is included by these two
> files? The exceptions, such as 'log' and 'gc.pid', could be marked by
> a special character in the entry ("!gc.pid", for example) or any such
> scheme.
It might work. Let me try.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v5 00/28] Support multiple checkouts
2014-03-01 12:12 ` [PATCH v4 00/27] " Nguyễn Thái Ngọc Duy
` (26 preceding siblings ...)
2014-03-01 12:13 ` [PATCH v4 27/27] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
` (29 more replies)
27 siblings, 30 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The diff against v4 is kinda big but it's mostly about converting
`...` to $(...) and making git_path() and friends return a const
string.
Another notable change is I no longer attempt to support checkouts on
portable devices. Torsten pointed out (privately) that my dealing with
Windows drives was insufficient. And Junio was not so happy with how
link() was handled either. We can revisit it later.
Many thanks to Eric, who was very patient to go through the series
carefully and pointed out problems that I overlooked.
v4..v5 diff below for convenience
-- 8< --
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 438b213..c635b8d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1184,9 +1184,10 @@ gc.pruneexpire::
unreachable objects immediately.
gc.prunereposexpire::
- When 'git gc' is run, it will call 'prune --repos --expire 3.months.ago'.
- Override the grace period with this config variable. The value
- "now" may be used to disable this grace period and always prune
+ When 'git gc' is run, it will call
+ 'prune --repos --expire 3.months.ago'.
+ Override the grace period with this config variable. The value
+ "now" may be used to disable the grace period and always prune
$GIT_DIR/repos immediately.
gc.reflogexpire::
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 8e6ad32..8a9cac8 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -238,7 +238,9 @@ print a message to stderr and exit with nonzero status.
--git-path <path>::
Resolve "$GIT_DIR/<path>" and takes other path relocation
variables such as $GIT_OBJECT_DIRECTORY,
- $GIT_INDEX_FILE... into account.
+ $GIT_INDEX_FILE... into account. For example, if
+ $GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
+ --git-path objects/abc" returns /tmp/bar/abc.
--show-cdup::
When the command is invoked from a subdirectory, show the
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 2dc6901..dff6c47 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -47,7 +47,7 @@ use with dumb transports but otherwise is OK as long as
`objects/info/alternates` points at the object stores it
borrows from.
+
-This directory is ignored $GIT_COMMON_DIR is set and
+This directory is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/objects" will be used instead.
objects/[0-9a-f][0-9a-f]::
@@ -95,7 +95,7 @@ refs::
References are stored in subdirectories of this
directory. The 'git prune' command knows to preserve
objects reachable from refs found in this directory and
- its subdirectories. This directory is ignored $GIT_COMMON_DIR
+ its subdirectories. This directory is ignored if $GIT_COMMON_DIR
is set and "$GIT_COMMON_DIR/refs" will be used instead.
refs/heads/`name`::
@@ -118,7 +118,7 @@ refs/replace/`<obj-sha1>`::
packed-refs::
records the same information as refs/heads/, refs/tags/,
and friends record in a more efficient way. See
- linkgit:git-pack-refs[1]. This file is ignored $GIT_COMMON_DIR
+ linkgit:git-pack-refs[1]. This file is ignored if $GIT_COMMON_DIR
is set and "$GIT_COMMON_DIR/packed-refs" will be used instead.
HEAD::
@@ -151,7 +151,7 @@ branches::
'repository' argument. See the REMOTES section in
linkgit:git-fetch[1] for details. This mechanism is legacy
and not likely to be found in modern repositories. This
- directory is ignored $GIT_COMMON_DIR is set and
+ directory is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/branches" will be used instead.
@@ -162,7 +162,7 @@ hooks::
default. To enable, the `.sample` suffix has to be
removed from the filename by renaming.
Read linkgit:githooks[5] for more details about
- each hook. This directory is ignored $GIT_COMMON_DIR is set
+ each hook. This directory is ignored if $GIT_COMMON_DIR is set
and "$GIT_COMMON_DIR/hooks" will be used instead.
@@ -172,7 +172,7 @@ index::
info::
Additional information about the repository is recorded
- in this directory. This directory is ignored $GIT_COMMON_DIR
+ in this directory. This directory is ignored if $GIT_COMMON_DIR
is set and "$GIT_COMMON_DIR/index" will be used instead.
info/refs::
@@ -210,7 +210,7 @@ remotes::
'git pull' and 'git push' commands. See the REMOTES section
in linkgit:git-fetch[1] for details. This mechanism is legacy
and not likely to be found in modern repositories. This
- directory is ignored $GIT_COMMON_DIR is set and
+ directory is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/remotes" will be used instead.
logs::
@@ -230,7 +230,7 @@ shallow::
This is similar to `info/grafts` but is internally used
and maintained by shallow clone mechanism. See `--depth`
option to linkgit:git-clone[1] and linkgit:git-fetch[1]. This
- file is ignored $GIT_COMMON_DIR is set and
+ file is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/shallow" will be used instead.
commondir::
@@ -242,7 +242,7 @@ commondir::
modules::
Contains the git-repositories of the submodules. This
- directory is ignored $GIT_COMMON_DIR is set and
+ directory is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/modules" will be used instead.
repos::
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 24b7f36..9dc80f1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -599,9 +599,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (opts->new_branch_log && !log_all_ref_updates) {
int temp;
struct strbuf log_file = STRBUF_INIT;
- char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
int ret;
+ const char *ref_name;
+ ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
temp = log_all_ref_updates;
log_all_ref_updates = 1;
ret = log_ref_setup(ref_name, &log_file);
@@ -843,8 +844,8 @@ static void remove_junk(void)
if (junk_work_tree) {
strbuf_addstr(&sb, junk_work_tree);
remove_dir_recursively(&sb, 0);
- strbuf_reset(&sb);
}
+ strbuf_release(&sb);
}
static void remove_junk_on_signal(int signo)
@@ -854,17 +855,6 @@ static void remove_junk_on_signal(int signo)
raise(signo);
}
-static dev_t get_device_or_die(const char *path)
-{
- struct stat buf;
- if (stat(path, &buf))
- die_errno("failed to stat '%s'", path);
- /* Ah Windows! Make different drives different "partitions" */
- if (is_windows())
- buf.st_dev = toupper(real_path(path)[0]);
- return buf.st_dev;
-}
-
static int prepare_linked_checkout(const struct checkout_opts *opts,
struct branch_info *new)
{
@@ -873,7 +863,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
const char *path = opts->new_worktree, *name;
struct stat st;
struct child_process cp;
- int counter = 0, len, keep_locked = 0, ret;
+ int counter = 0, len, ret;
if (!new->commit)
die(_("no branch specified"));
@@ -909,6 +899,10 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
junk_git_dir = sb_repo.buf;
is_junk = 1;
+ /*
+ * lock the incomplete repo so prunt won't delete it, unlock
+ * after the preparation is over.
+ */
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
write_file(sb.buf, 1, "initializing\n");
@@ -936,18 +930,6 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, 1, "../..\n");
- if (get_device_or_die(path) != get_device_or_die(get_git_dir())) {
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s/locked", sb_repo.buf);
- write_file(sb.buf, 1, "located on a different file system\n");
- keep_locked = 1;
- } else {
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s/link", sb_repo.buf);
- if (link(sb_git.buf, sb.buf))
- ; /* ignore silently, without a clear reason */
- }
-
if (!opts->quiet)
fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
@@ -960,11 +942,9 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
ret = run_command(&cp);
if (!ret)
is_junk = 0;
- if (!keep_locked) {
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s/locked", sb_repo.buf);
- unlink_or_warn(sb.buf);
- }
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ unlink_or_warn(sb.buf);
strbuf_release(&sb);
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
diff --git a/builtin/clone.c b/builtin/clone.c
index 43e772c..9339371 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -289,16 +289,17 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, in, '\n') != EOF) {
- char *abs_path, abs_buf[PATH_MAX];
+ char *abs_path;
if (!line.len || line.buf[0] == '#')
continue;
if (is_absolute_path(line.buf)) {
add_to_alternates_file(line.buf);
continue;
}
- abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
- normalize_path_copy(abs_buf, abs_path);
- add_to_alternates_file(abs_buf);
+ abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+ normalize_path_copy(abs_path, abs_path);
+ add_to_alternates_file(abs_path);
+ free(abs_path);
}
strbuf_release(&line);
fclose(in);
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 725cd5f..d3a1620 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -78,39 +78,6 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
}
}
-static void report_linked_checkout_garbage(void)
-{
- /*
- * must be more or less in sync with * path.c:update_common_dir().
- *
- * "logs" is let slip because logs/HEAD is in $GIT_DIR but the
- * remaining in $GIT_COMMON_DIR. Probably not worth traversing
- * the entire "logs" directory for that.
- *
- * The same "gc.pid" for because it's a temporary file.
- */
- const char *list[] = {
- "branches", "hooks", "info", "lost-found", "modules",
- "objects", "refs", "remotes", "rr-cache", "svn",
- "config", "packed-refs", "shallow", NULL
- };
- struct strbuf sb = STRBUF_INIT;
- const char **p;
- int len;
-
- if (!file_exists(git_path("commondir")))
- return;
- strbuf_addf(&sb, "%s/", get_git_dir());
- len = sb.len;
- for (p = list; *p; p++) {
- strbuf_setlen(&sb, len);
- strbuf_addstr(&sb, *p);
- if (file_exists(sb.buf))
- report_garbage("unused in linked checkout", sb.buf);
- }
- strbuf_release(&sb);
-}
-
static char const * const count_objects_usage[] = {
N_("git count-objects [-v] [-H | --human-readable]"),
NULL
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 025bc3e..04b51ca 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -544,7 +544,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
- char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+ char *url;
+ const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
int want_status;
fp = fopen(filename, "a");
@@ -778,7 +779,7 @@ static void check_not_current_branch(struct ref *ref_map)
static int truncate_fetch_head(void)
{
- char *filename = git_path("FETCH_HEAD");
+ const char *filename = git_path("FETCH_HEAD");
FILE *fp = fopen(filename, "w");
if (!fp)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 1affdd5..643c980 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -225,12 +225,12 @@ static void check_unreachable_object(struct object *obj)
printf("dangling %s %s\n", typename(obj->type),
sha1_to_hex(obj->sha1));
if (write_lost_and_found) {
- char *filename = git_path("lost-found/%s/%s",
+ const char *filename = git_path("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
sha1_to_hex(obj->sha1));
FILE *f;
- if (safe_create_leading_directories(filename)) {
+ if (safe_create_leading_directories_const(filename)) {
error("Could not create lost-found");
return;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 85bba35..4d27acb 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -599,7 +599,7 @@ static void run_update_post_hook(struct command *commands)
int argc;
const char **argv;
struct child_process proc;
- char *hook;
+ const char *hook;
hook = find_hook("post-update");
for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/remote.c b/builtin/remote.c
index b3ab4cf..ca3cb3b 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -582,7 +582,7 @@ static int migrate_file(struct remote *remote)
{
struct strbuf buf = STRBUF_INIT;
int i;
- char *path = NULL;
+ const char *path = NULL;
strbuf_addf(&buf, "remote.%s.url", remote->name);
for (i = 0; i < remote->url_nr; i++)
diff --git a/builtin/repack.c b/builtin/repack.c
index bb2314c..4d35349 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -257,7 +257,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
failed = 0;
for_each_string_list_item(item, &names) {
for (ext = 0; ext < 2; ext++) {
- char *fname, *fname_old;
+ const char *fname_old;
+ char *fname;
fname = mkpathdup("%s/pack-%s%s", packdir,
item->string, exts[ext]);
if (!file_exists(fname)) {
@@ -285,7 +286,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (failed) {
struct string_list rollback_failure = STRING_LIST_INIT_DUP;
for_each_string_list_item(item, &rollback) {
- char *fname, *fname_old;
+ const char *fname_old;
+ char *fname;
fname = mkpathdup("%s/%s", packdir, item->string);
fname_old = mkpath("%s/old-%s", packdir, item->string);
if (rename(fname_old, fname))
@@ -334,7 +336,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
/* Remove the "old-" files */
for_each_string_list_item(item, &names) {
for (ext = 0; ext < 2; ext++) {
- char *fname;
+ const char *fname;
fname = mkpath("%s/old-%s%s",
packdir,
item->string,
diff --git a/cache.h b/cache.h
index 99b86d9..a0ff430 100644
--- a/cache.h
+++ b/cache.h
@@ -658,10 +658,11 @@ extern char *mkpathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
/* Return a statically allocated filename matching the sha1 signature */
-extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path_submodule(const char *path, const char *fmt, ...)
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path_submodule(const char *path, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+extern void report_linked_checkout_garbage(void);
extern char *sha1_file_name(const unsigned char *sha1);
extern char *sha1_pack_name(const unsigned char *sha1);
diff --git a/compat/mingw.h b/compat/mingw.h
index 18323c1..e033e72 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -328,7 +328,6 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
* git specific compatibility
*/
-#define is_windows() 1
#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
static inline char *mingw_find_last_dir_sep(const char *path)
diff --git a/fast-import.c b/fast-import.c
index 08a1e78..28e7a63 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -403,7 +403,7 @@ static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
static void write_crash_report(const char *err)
{
- char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+ const char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
FILE *rpt = fopen(loc, "w");
struct branch *b;
unsigned long lu;
diff --git a/git-am.sh b/git-am.sh
index dfa0618..10babd2 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -803,7 +803,7 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
continue
fi
- hook="`git rev-parse --git-path hooks/applypatch-msg`"
+ hook="$(git rev-parse --git-path hooks/applypatch-msg)"
if test -x "$hook"
then
"$hook" "$dotest/final-commit" || stop_here $this
@@ -880,7 +880,7 @@ did you forget to use 'git add'?"
stop_here_user_resolve $this
fi
- hook="`git rev-parse --git-path hooks/pre-applypatch`"
+ hook="$(git rev-parse --git-path hooks/pre-applypatch)"
if test -x "$hook"
then
"$hook" || stop_here $this
@@ -909,7 +909,7 @@ did you forget to use 'git add'?"
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
fi
- hook="`git rev-parse --git-path hooks/post-applypatch`"
+ hook="$(git rev-parse --git-path hooks/post-applypatch)"
test -x "$hook" && "$hook"
go_next
@@ -917,7 +917,7 @@ done
if test -s "$dotest"/rewritten; then
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
- hook="`git rev-parse --git-path hooks/post-rewrite`"
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
if test -x "$hook"; then
"$hook" rebase < "$dotest"/rewritten
fi
diff --git a/git-compat-util.h b/git-compat-util.h
index 41f1b74..cbd86c3 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -266,10 +266,6 @@ extern char *gitbasename(char *);
#define STRIP_EXTENSION ""
#endif
-#ifndef is_windows
-#define is_windows() 0
-#endif
-
#ifndef has_dos_drive_prefix
#define has_dos_drive_prefix(path) 0
#endif
diff --git a/git-pull.sh b/git-pull.sh
index c9dc9ba..2a90262 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -218,7 +218,7 @@ test true = "$rebase" && {
if ! git rev-parse -q --verify HEAD >/dev/null
then
# On an unborn branch
- if test -f "`git rev-parse --git-path index`"
+ if test -f "$(git rev-parse --git-path index)"
then
die "$(gettext "updating an unborn branch with changes added to the index")"
fi
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index d741b04..dd84f9d 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -632,7 +632,7 @@ do_next () {
git notes copy --for-rewrite=rebase < "$rewritten_list" ||
true # we don't care if this copying failed
} &&
- hook="`git rev-parse --git-path hooks/post-rewrite`"
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
if test -x "$hook" && test -s "$rewritten_list"; then
"$hook" rebase < "$rewritten_list"
true # we don't care if this hook failed
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index 68f5d09..93d7bcf 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -93,7 +93,7 @@ finish_rb_merge () {
if test -s "$state_dir"/rewritten
then
git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
- hook="`git rev-parse --git-path hooks/post-rewrite`"
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
fi
say All done.
diff --git a/git-rebase.sh b/git-rebase.sh
index 1cf8dba..b90a6d9 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -195,9 +195,9 @@ run_specific_rebase () {
run_pre_rebase_hook () {
if test -z "$ok_to_skip_pre_rebase" &&
- test -x "`git rev-parse --git-path hooks/pre-rebase`"
+ test -x "$(git rev-parse --git-path hooks/pre-rebase)"
then
- "`git rev-parse --git-path hooks/pre-rebase`" ${1+"$@"} ||
+ "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
die "$(gettext "The pre-rebase hook refused to rebase.")"
fi
}
diff --git a/git-stash.sh b/git-stash.sh
index 12d9b37..8093326 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -20,7 +20,7 @@ require_work_tree
cd_to_toplevel
TMP="$GIT_DIR/.git-stash.$$"
-TMPindex=${GIT_INDEX_FILE-"`git rev-parse --git-path index`"}.stash.$$
+TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
trap 'rm -f "$TMP-"* "$TMPindex"' 0
ref_stash=refs/stash
@@ -183,7 +183,7 @@ store_stash () {
fi
# Make sure the reflog for stash is kept.
- : >>"`git rev-parse --git-path logs/$ref_stash`"
+ : >>"$(git rev-parse --git-path logs/$ref_stash)"
git update-ref -m "$stash_msg" $ref_stash $w_commit
ret=$?
test $ret != 0 && test -z $quiet &&
@@ -258,7 +258,7 @@ save_stash () {
say "$(gettext "No local changes to save")"
exit 0
fi
- test -f "`git rev-parse --git-path logs/$ref_stash`" ||
+ test -f "$(git rev-parse --git-path logs/$ref_stash)" ||
clear_stash || die "$(gettext "Cannot initialize stash")"
create_stash "$stash_msg" $untracked
diff --git a/notes-merge.c b/notes-merge.c
index 94a1a8a..d59bcc2 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -280,7 +280,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
"(%s exists).", git_path("NOTES_MERGE_*"));
}
- if (safe_create_leading_directories(git_path(
+ if (safe_create_leading_directories_const(git_path(
NOTES_MERGE_WORKTREE "/.test")))
die_errno("unable to create directory %s",
git_path(NOTES_MERGE_WORKTREE));
@@ -295,8 +295,8 @@ static void write_buf_to_worktree(const unsigned char *obj,
const char *buf, unsigned long size)
{
int fd;
- char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
- if (safe_create_leading_directories(path))
+ const char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+ if (safe_create_leading_directories_const(path))
die_errno("unable to create directory for '%s'", path);
if (file_exists(path))
die("found existing file at '%s'", path);
diff --git a/path.c b/path.c
index 2e6035d..5a7dc45 100644
--- a/path.c
+++ b/path.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "strbuf.h"
#include "string-list.h"
+#include "dir.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -90,35 +91,60 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
buf->buf[newlen] = '/';
}
+static const char *common_list[] = {
+ "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules",
+ "/objects", "/refs", "/remotes", "/repos", "/rr-cache", "/svn",
+ "config", "!gc.pid", "packed-refs", "shallow",
+ NULL
+};
+
static void update_common_dir(struct strbuf *buf, int git_dir_len)
{
- /*
- * Remember to report_linked_checkout_garbage()
- * builtin/count-objects.c
- */
- const char *common_dir_list[] = {
- "branches", "hooks", "info", "logs", "lost-found", "modules",
- "objects", "refs", "remotes", "repos", "rr-cache", "svn",
- NULL
- };
- const char *common_top_file_list[] = {
- "config", "gc.pid", "packed-refs", "shallow", NULL
- };
char *base = buf->buf + git_dir_len;
const char **p;
if (is_dir_file(base, "logs", "HEAD"))
return; /* keep this in $GIT_DIR */
- for (p = common_dir_list; *p; p++)
- if (dir_prefix(base, *p)) {
+ for (p = common_list; *p; p++) {
+ const char *path = *p;
+ int is_dir = 0;
+ if (*path == '!')
+ path++;
+ if (*path == '/') {
+ path++;
+ is_dir = 1;
+ }
+ if (is_dir && dir_prefix(base, path)) {
replace_dir(buf, git_dir_len, get_git_common_dir());
return;
}
- for (p = common_top_file_list; *p; p++)
- if (!strcmp(base, *p)) {
+ if (!is_dir && !strcmp(base, path)) {
replace_dir(buf, git_dir_len, get_git_common_dir());
return;
}
+ }
+}
+
+void report_linked_checkout_garbage(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char **p;
+ int len;
+
+ if (!git_common_dir_env)
+ return;
+ strbuf_addf(&sb, "%s/", get_git_dir());
+ len = sb.len;
+ for (p = common_list; *p; p++) {
+ const char *path = *p;
+ if (*path == '!')
+ continue;
+ strbuf_setlen(&sb, len);
+ strbuf_addstr(&sb, path);
+ if (file_exists(sb.buf))
+ report_garbage("unused in linked checkout", sb.buf);
+ }
+ strbuf_release(&sb);
}
static void adjust_git_path(struct strbuf *buf, int git_dir_len)
@@ -138,9 +164,8 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
{
- const char *git_dir = get_git_dir();
int gitdir_len;
- strbuf_addstr(buf, git_dir);
+ strbuf_addstr(buf, get_git_dir());
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
gitdir_len = buf->len;
@@ -157,7 +182,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
va_end(args);
}
-char *git_path(const char *fmt, ...)
+const char *git_path(const char *fmt, ...)
{
struct strbuf *pathname = get_pathname();
va_list args;
@@ -188,7 +213,7 @@ char *mkpathdup(const char *fmt, ...)
return strbuf_detach(&sb, NULL);
}
-char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
{
va_list args;
struct strbuf *pathname = get_pathname();
@@ -224,7 +249,7 @@ void home_config_paths(char **global, char **xdg, char *file)
free(to_free);
}
-char *git_path_submodule(const char *path, const char *fmt, ...)
+const char *git_path_submodule(const char *path, const char *fmt, ...)
{
struct strbuf *buf = get_pathname();
const char *git_dir;
diff --git a/refs.c b/refs.c
index 434bd5e..c5613b0 100644
--- a/refs.c
+++ b/refs.c
@@ -1232,7 +1232,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
{
int fd, len;
char buffer[128], *p;
- char *path;
+ const char *path;
if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
return -1;
@@ -2044,7 +2044,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- char *ref_file;
+ const char *ref_file;
const char *orig_refname = refname;
struct ref_lock *lock;
int last_errno = 0;
@@ -2107,7 +2107,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock->force_write = 1;
retry:
- switch (safe_create_leading_directories(ref_file)) {
+ switch (safe_create_leading_directories_const(ref_file)) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
@@ -2545,7 +2545,7 @@ static int rename_tmp_log(const char *newrefname)
int attempts_remaining = 4;
retry:
- switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+ switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
@@ -2729,43 +2729,41 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
-int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
+int log_ref_setup(const char *refname, struct strbuf *logfile)
{
int logfd, oflags = O_APPEND | O_WRONLY;
- const char *logfile;
- strbuf_git_path(sb_logfile, "logs/%s", refname);
- logfile = sb_logfile->buf;
+ strbuf_git_path(logfile, "logs/%s", refname);
if (log_all_ref_updates &&
(starts_with(refname, "refs/heads/") ||
starts_with(refname, "refs/remotes/") ||
starts_with(refname, "refs/notes/") ||
!strcmp(refname, "HEAD"))) {
- if (safe_create_leading_directories(sb_logfile->buf) < 0)
+ if (safe_create_leading_directories(logfile->buf) < 0)
return error("unable to create directory for %s",
- logfile);
+ logfile->buf);
oflags |= O_CREAT;
}
- logfd = open(logfile, oflags, 0666);
+ logfd = open(logfile->buf, oflags, 0666);
if (logfd < 0) {
if (!(oflags & O_CREAT) && errno == ENOENT)
return 0;
if ((oflags & O_CREAT) && errno == EISDIR) {
- if (remove_empty_directories(logfile)) {
+ if (remove_empty_directories(logfile->buf)) {
return error("There are still logs under '%s'",
- logfile);
+ logfile->buf);
}
- logfd = open(logfile, oflags, 0666);
+ logfd = open(logfile->buf, oflags, 0666);
}
if (logfd < 0)
return error("Unable to append to %s: %s",
- logfile, strerror(errno));
+ logfile->buf, strerror(errno));
}
- adjust_shared_perm(logfile);
+ adjust_shared_perm(logfile->buf);
close(logfd);
return 0;
}
diff --git a/run-command.c b/run-command.c
index 3914d9c..a94a612 100644
--- a/run-command.c
+++ b/run-command.c
@@ -751,9 +751,9 @@ int finish_async(struct async *async)
#endif
}
-char *find_hook(const char *name)
+const char *find_hook(const char *name)
{
- char *path = git_path("hooks/%s", name);
+ const char *path = git_path("hooks/%s", name);
if (access(path, X_OK) < 0)
path = NULL;
diff --git a/run-command.h b/run-command.h
index 6b985af..da01954 100644
--- a/run-command.h
+++ b/run-command.h
@@ -45,7 +45,7 @@ int start_command(struct child_process *);
int finish_command(struct child_process *);
int run_command(struct child_process *);
-extern char *find_hook(const char *name);
+extern const char *find_hook(const char *name);
LAST_ARG_MUST_BE_NULL
extern int run_hook(const char *index_file, const char *name, ...);
diff --git a/sha1_file.c b/sha1_file.c
index 6e8c05d..8af0e18 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -402,7 +402,7 @@ void add_to_alternates_file(const char *reference)
{
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
- char *alt = mkpath("%s\n", reference);
+ const char *alt = mkpath("%s\n", reference);
write_or_die(fd, alt, strlen(alt));
if (commit_lock_file(lock))
die("could not close alternates file");
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
index 28b843b..a5d7b84 100755
--- a/templates/hooks--applypatch-msg.sample
+++ b/templates/hooks--applypatch-msg.sample
@@ -10,6 +10,6 @@
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
-commitmsg="`git rev-parse --git-path hooks/commit-msg`"
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
index 51aa244..4142082 100755
--- a/templates/hooks--pre-applypatch.sample
+++ b/templates/hooks--pre-applypatch.sample
@@ -9,6 +9,6 @@
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
-precommit="`git rev-parse --git-path hooks/pre-commit`"
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
diff --git a/wrapper.c b/wrapper.c
index 5ced50d..5ad628c 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -459,8 +459,8 @@ struct passwd *xgetpwuid_self(void)
int write_file(const char *path, int fatal, const char *fmt, ...)
{
struct strbuf sb = STRBUF_INIT;
- int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
va_list params;
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
if (fatal)
die_errno(_("could not open %s for writing"), path);
@@ -472,8 +472,8 @@ int write_file(const char *path, int fatal, const char *fmt, ...)
if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
int err = errno;
close(fd);
- errno = err;
strbuf_release(&sb);
+ errno = err;
if (fatal)
die_errno(_("could not write to %s"), path);
return -1;
-- 8< --
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 6:11 ` Torsten Bögershausen
2014-03-08 2:47 ` [PATCH v5 02/28] path.c: make get_pathname() call sites return const char * Nguyễn Thái Ngọc Duy
` (28 subsequent siblings)
29 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
We've been avoiding PATH_MAX whenever possible. This patch makes
get_pathname() return a strbuf and updates the callers to take
advantage of this. The code is simplified as we no longer need to
worry about buffer overflow.
vsnpath() behavior is changed slightly: previously it always clears
the buffer before writing, now it just appends. Fortunately this is a
static function and all of its callers prepare the buffer properly:
git_path() gets the buffer from get_pathname() which resets the
buffer, the remaining call sites start with STRBUF_INIT'd buffer.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 120 ++++++++++++++++++++++++++++-------------------------------------
1 file changed, 51 insertions(+), 69 deletions(-)
diff --git a/path.c b/path.c
index 24594c4..5346700 100644
--- a/path.c
+++ b/path.c
@@ -16,11 +16,15 @@ static int get_st_mode_bits(const char *path, int *mode)
static char bad_path[] = "/bad-path/";
-static char *get_pathname(void)
+static struct strbuf *get_pathname(void)
{
- static char pathname_array[4][PATH_MAX];
+ static struct strbuf pathname_array[4] = {
+ STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+ };
static int index;
- return pathname_array[3 & ++index];
+ struct strbuf *sb = &pathname_array[3 & ++index];
+ strbuf_reset(sb);
+ return sb;
}
static char *cleanup_path(char *path)
@@ -34,6 +38,13 @@ static char *cleanup_path(char *path)
return path;
}
+static void strbuf_cleanup_path(struct strbuf *sb)
+{
+ char *path = cleanup_path(sb->buf);
+ if (path > sb->buf)
+ strbuf_remove(sb, 0, path - sb->buf);
+}
+
char *mksnpath(char *buf, size_t n, const char *fmt, ...)
{
va_list args;
@@ -49,85 +60,70 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
-static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
{
const char *git_dir = get_git_dir();
- size_t len;
-
- len = strlen(git_dir);
- if (n < len + 1)
- goto bad;
- memcpy(buf, git_dir, len);
- if (len && !is_dir_sep(git_dir[len-1]))
- buf[len++] = '/';
- len += vsnprintf(buf + len, n - len, fmt, args);
- if (len >= n)
- goto bad;
- return cleanup_path(buf);
-bad:
- strlcpy(buf, bad_path, n);
- return buf;
+ strbuf_addstr(buf, git_dir);
+ if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+ strbuf_addch(buf, '/');
+ strbuf_vaddf(buf, fmt, args);
+ strbuf_cleanup_path(buf);
}
char *git_snpath(char *buf, size_t n, const char *fmt, ...)
{
- char *ret;
+ struct strbuf sb = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- ret = vsnpath(buf, n, fmt, args);
+ vsnpath(&sb, fmt, args);
va_end(args);
- return ret;
+ if (sb.len >= n)
+ strlcpy(buf, bad_path, n);
+ else
+ memcpy(buf, sb.buf, sb.len + 1);
+ strbuf_release(&sb);
+ return buf;
}
char *git_pathdup(const char *fmt, ...)
{
- char path[PATH_MAX], *ret;
+ struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- ret = vsnpath(path, sizeof(path), fmt, args);
+ vsnpath(&path, fmt, args);
va_end(args);
- return xstrdup(ret);
+ return strbuf_detach(&path, NULL);
}
char *mkpathdup(const char *fmt, ...)
{
- char *path;
struct strbuf sb = STRBUF_INIT;
va_list args;
-
va_start(args, fmt);
strbuf_vaddf(&sb, fmt, args);
va_end(args);
- path = xstrdup(cleanup_path(sb.buf));
-
- strbuf_release(&sb);
- return path;
+ strbuf_cleanup_path(&sb);
+ return strbuf_detach(&sb, NULL);
}
char *mkpath(const char *fmt, ...)
{
va_list args;
- unsigned len;
- char *pathname = get_pathname();
-
+ struct strbuf *pathname = get_pathname();
va_start(args, fmt);
- len = vsnprintf(pathname, PATH_MAX, fmt, args);
+ strbuf_vaddf(pathname, fmt, args);
va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
+ return cleanup_path(pathname->buf);
}
char *git_path(const char *fmt, ...)
{
- char *pathname = get_pathname();
+ struct strbuf *pathname = get_pathname();
va_list args;
- char *ret;
-
va_start(args, fmt);
- ret = vsnpath(pathname, PATH_MAX, fmt, args);
+ vsnpath(pathname, fmt, args);
va_end(args);
- return ret;
+ return pathname->buf;
}
void home_config_paths(char **global, char **xdg, char *file)
@@ -158,41 +154,27 @@ void home_config_paths(char **global, char **xdg, char *file)
char *git_path_submodule(const char *path, const char *fmt, ...)
{
- char *pathname = get_pathname();
- struct strbuf buf = STRBUF_INIT;
+ struct strbuf *buf = get_pathname();
const char *git_dir;
va_list args;
- unsigned len;
-
- len = strlen(path);
- if (len > PATH_MAX-100)
- return bad_path;
- strbuf_addstr(&buf, path);
- if (len && path[len-1] != '/')
- strbuf_addch(&buf, '/');
- strbuf_addstr(&buf, ".git");
+ strbuf_addstr(buf, path);
+ if (buf->len && buf->buf[buf->len - 1] != '/')
+ strbuf_addch(buf, '/');
+ strbuf_addstr(buf, ".git");
- git_dir = read_gitfile(buf.buf);
+ git_dir = read_gitfile(buf->buf);
if (git_dir) {
- strbuf_reset(&buf);
- strbuf_addstr(&buf, git_dir);
+ strbuf_reset(buf);
+ strbuf_addstr(buf, git_dir);
}
- strbuf_addch(&buf, '/');
-
- if (buf.len >= PATH_MAX)
- return bad_path;
- memcpy(pathname, buf.buf, buf.len + 1);
-
- strbuf_release(&buf);
- len = strlen(pathname);
+ strbuf_addch(buf, '/');
va_start(args, fmt);
- len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+ strbuf_vaddf(buf, fmt, args);
va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
+ strbuf_cleanup_path(buf);
+ return buf->buf;
}
int validate_headref(const char *path)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-08 2:47 ` [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
@ 2014-03-08 6:11 ` Torsten Bögershausen
2014-03-08 8:57 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Torsten Bögershausen @ 2014-03-08 6:11 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano
On 2014-03-08 03.47, Nguyễn Thái Ngọc Duy wrote:
> We've been avoiding PATH_MAX whenever possible. This patch makes
> get_pathname() return a strbuf and updates the callers to take
> advantage of this. The code is simplified as we no longer need to
> worry about buffer overflow.
>
> vsnpath() behavior is changed slightly: previously it always clears
Minor question:
Is the function name vsnpath() appropriate any more ?
How about renaming the function into strbuf_vaddpath() ?
> the buffer before writing, now it just appends. Fortunately this is a
> static function and all of its callers prepare the buffer properly:
> git_path() gets the buffer from get_pathname() which resets the
> buffer, the remaining call sites start with STRBUF_INIT'd buffer.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer
2014-03-08 6:11 ` Torsten Bögershausen
@ 2014-03-08 8:57 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-08 8:57 UTC (permalink / raw)
To: Torsten Bögershausen; +Cc: Git Mailing List, Junio C Hamano
On Sat, Mar 8, 2014 at 1:11 PM, Torsten Bögershausen <tboegi@web.de> wrote:
> On 2014-03-08 03.47, Nguyễn Thái Ngọc Duy wrote:
>> We've been avoiding PATH_MAX whenever possible. This patch makes
>> get_pathname() return a strbuf and updates the callers to take
>> advantage of this. The code is simplified as we no longer need to
>> worry about buffer overflow.
>>
>> vsnpath() behavior is changed slightly: previously it always clears
> Minor question:
> Is the function name vsnpath() appropriate any more ?
> How about renaming the function into strbuf_vaddpath() ?
It is renamed in 04/28 to do_git_path().
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v5 02/28] path.c: make get_pathname() call sites return const char *
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 03/28] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
` (27 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Before the previous commit, get_pathname returns an array of PATH_MAX
length. Even if git_path() and similar functions does not use the
whole array, git_path() caller can, in theory.
After the commit, get_pathname() may return a buffer that has just
enough room for the returned string and git_path() caller should never
write beyond that.
Make git_path(), mkpath() and git_path_submodule() return a const
buffer to make sure callers do not write in it at all.
This could have been part of the previous commit, but the "const"
conversion is too much distraction from the core changes in path.c.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 2 +-
builtin/clone.c | 9 +++++----
builtin/fetch.c | 5 +++--
builtin/fsck.c | 4 ++--
builtin/receive-pack.c | 2 +-
builtin/remote.c | 2 +-
builtin/repack.c | 8 +++++---
cache.h | 6 +++---
fast-import.c | 2 +-
notes-merge.c | 6 +++---
path.c | 6 +++---
refs.c | 8 ++++----
run-command.c | 4 ++--
run-command.h | 2 +-
sha1_file.c | 2 +-
15 files changed, 36 insertions(+), 32 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5df3837..64c2aca 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -586,7 +586,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (opts->new_branch_log && !log_all_ref_updates) {
int temp;
char log_file[PATH_MAX];
- char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+ const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
temp = log_all_ref_updates;
log_all_ref_updates = 1;
diff --git a/builtin/clone.c b/builtin/clone.c
index 43e772c..9339371 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -289,16 +289,17 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, in, '\n') != EOF) {
- char *abs_path, abs_buf[PATH_MAX];
+ char *abs_path;
if (!line.len || line.buf[0] == '#')
continue;
if (is_absolute_path(line.buf)) {
add_to_alternates_file(line.buf);
continue;
}
- abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
- normalize_path_copy(abs_buf, abs_path);
- add_to_alternates_file(abs_buf);
+ abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+ normalize_path_copy(abs_path, abs_path);
+ add_to_alternates_file(abs_path);
+ free(abs_path);
}
strbuf_release(&line);
fclose(in);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 025bc3e..04b51ca 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -544,7 +544,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
- char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+ char *url;
+ const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
int want_status;
fp = fopen(filename, "a");
@@ -778,7 +779,7 @@ static void check_not_current_branch(struct ref *ref_map)
static int truncate_fetch_head(void)
{
- char *filename = git_path("FETCH_HEAD");
+ const char *filename = git_path("FETCH_HEAD");
FILE *fp = fopen(filename, "w");
if (!fp)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 1affdd5..643c980 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -225,12 +225,12 @@ static void check_unreachable_object(struct object *obj)
printf("dangling %s %s\n", typename(obj->type),
sha1_to_hex(obj->sha1));
if (write_lost_and_found) {
- char *filename = git_path("lost-found/%s/%s",
+ const char *filename = git_path("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
sha1_to_hex(obj->sha1));
FILE *f;
- if (safe_create_leading_directories(filename)) {
+ if (safe_create_leading_directories_const(filename)) {
error("Could not create lost-found");
return;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 85bba35..4d27acb 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -599,7 +599,7 @@ static void run_update_post_hook(struct command *commands)
int argc;
const char **argv;
struct child_process proc;
- char *hook;
+ const char *hook;
hook = find_hook("post-update");
for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/remote.c b/builtin/remote.c
index b3ab4cf..ca3cb3b 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -582,7 +582,7 @@ static int migrate_file(struct remote *remote)
{
struct strbuf buf = STRBUF_INIT;
int i;
- char *path = NULL;
+ const char *path = NULL;
strbuf_addf(&buf, "remote.%s.url", remote->name);
for (i = 0; i < remote->url_nr; i++)
diff --git a/builtin/repack.c b/builtin/repack.c
index bb2314c..4d35349 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -257,7 +257,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
failed = 0;
for_each_string_list_item(item, &names) {
for (ext = 0; ext < 2; ext++) {
- char *fname, *fname_old;
+ const char *fname_old;
+ char *fname;
fname = mkpathdup("%s/pack-%s%s", packdir,
item->string, exts[ext]);
if (!file_exists(fname)) {
@@ -285,7 +286,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (failed) {
struct string_list rollback_failure = STRING_LIST_INIT_DUP;
for_each_string_list_item(item, &rollback) {
- char *fname, *fname_old;
+ const char *fname_old;
+ char *fname;
fname = mkpathdup("%s/%s", packdir, item->string);
fname_old = mkpath("%s/old-%s", packdir, item->string);
if (rename(fname_old, fname))
@@ -334,7 +336,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
/* Remove the "old-" files */
for_each_string_list_item(item, &names) {
for (ext = 0; ext < 2; ext++) {
- char *fname;
+ const char *fname;
fname = mkpath("%s/old-%s%s",
packdir,
item->string,
diff --git a/cache.h b/cache.h
index dc040fb..a344a5f 100644
--- a/cache.h
+++ b/cache.h
@@ -654,9 +654,9 @@ extern char *mkpathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
/* Return a statically allocated filename matching the sha1 signature */
-extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path_submodule(const char *path, const char *fmt, ...)
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path_submodule(const char *path, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
extern char *sha1_file_name(const unsigned char *sha1);
diff --git a/fast-import.c b/fast-import.c
index 4fd18a3..a9f328d 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -403,7 +403,7 @@ static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
static void write_crash_report(const char *err)
{
- char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+ const char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
FILE *rpt = fopen(loc, "w");
struct branch *b;
unsigned long lu;
diff --git a/notes-merge.c b/notes-merge.c
index 94a1a8a..d59bcc2 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -280,7 +280,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
"(%s exists).", git_path("NOTES_MERGE_*"));
}
- if (safe_create_leading_directories(git_path(
+ if (safe_create_leading_directories_const(git_path(
NOTES_MERGE_WORKTREE "/.test")))
die_errno("unable to create directory %s",
git_path(NOTES_MERGE_WORKTREE));
@@ -295,8 +295,8 @@ static void write_buf_to_worktree(const unsigned char *obj,
const char *buf, unsigned long size)
{
int fd;
- char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
- if (safe_create_leading_directories(path))
+ const char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+ if (safe_create_leading_directories_const(path))
die_errno("unable to create directory for '%s'", path);
if (file_exists(path))
die("found existing file at '%s'", path);
diff --git a/path.c b/path.c
index 5346700..36d461e 100644
--- a/path.c
+++ b/path.c
@@ -106,7 +106,7 @@ char *mkpathdup(const char *fmt, ...)
return strbuf_detach(&sb, NULL);
}
-char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
{
va_list args;
struct strbuf *pathname = get_pathname();
@@ -116,7 +116,7 @@ char *mkpath(const char *fmt, ...)
return cleanup_path(pathname->buf);
}
-char *git_path(const char *fmt, ...)
+const char *git_path(const char *fmt, ...)
{
struct strbuf *pathname = get_pathname();
va_list args;
@@ -152,7 +152,7 @@ void home_config_paths(char **global, char **xdg, char *file)
free(to_free);
}
-char *git_path_submodule(const char *path, const char *fmt, ...)
+const char *git_path_submodule(const char *path, const char *fmt, ...)
{
struct strbuf *buf = get_pathname();
const char *git_dir;
diff --git a/refs.c b/refs.c
index 89228e2..f846f2f 100644
--- a/refs.c
+++ b/refs.c
@@ -1232,7 +1232,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
{
int fd, len;
char buffer[128], *p;
- char *path;
+ const char *path;
if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
return -1;
@@ -2032,7 +2032,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- char *ref_file;
+ const char *ref_file;
const char *orig_refname = refname;
struct ref_lock *lock;
int last_errno = 0;
@@ -2095,7 +2095,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock->force_write = 1;
retry:
- switch (safe_create_leading_directories(ref_file)) {
+ switch (safe_create_leading_directories_const(ref_file)) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
@@ -2533,7 +2533,7 @@ static int rename_tmp_log(const char *newrefname)
int attempts_remaining = 4;
retry:
- switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+ switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
diff --git a/run-command.c b/run-command.c
index 3914d9c..a94a612 100644
--- a/run-command.c
+++ b/run-command.c
@@ -751,9 +751,9 @@ int finish_async(struct async *async)
#endif
}
-char *find_hook(const char *name)
+const char *find_hook(const char *name)
{
- char *path = git_path("hooks/%s", name);
+ const char *path = git_path("hooks/%s", name);
if (access(path, X_OK) < 0)
path = NULL;
diff --git a/run-command.h b/run-command.h
index 6b985af..da01954 100644
--- a/run-command.h
+++ b/run-command.h
@@ -45,7 +45,7 @@ int start_command(struct child_process *);
int finish_command(struct child_process *);
int run_command(struct child_process *);
-extern char *find_hook(const char *name);
+extern const char *find_hook(const char *name);
LAST_ARG_MUST_BE_NULL
extern int run_hook(const char *index_file, const char *name, ...);
diff --git a/sha1_file.c b/sha1_file.c
index 6e8c05d..8af0e18 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -402,7 +402,7 @@ void add_to_alternates_file(const char *reference)
{
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
- char *alt = mkpath("%s\n", reference);
+ const char *alt = mkpath("%s\n", reference);
write_or_die(fd, alt, strlen(alt));
if (commit_lock_file(lock))
die("could not close alternates file");
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 03/28] Convert git_snpath() to strbuf_git_path()
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 01/28] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 02/28] path.c: make get_pathname() call sites return const char * Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-17 18:11 ` Junio C Hamano
2014-03-08 2:47 ` [PATCH v5 04/28] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
` (26 subsequent siblings)
29 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In the previous patch, git_snpath() is modified to allocate a new
strbuf buffer because vsnpath() needs that. But that makes it awkward
because git_snpath() receives a pre-allocated buffer from outside and
has to copy data back. Rename it to strbuf_git_path() and make it
receive strbuf directly.
The conversion from git_snpath() to git_path() in
update_refs_for_switch() is safe because that function does not keep
any pointer to the round-robin buffer pool allocated by
get_pathname().
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 25 +++++++++--------
cache.h | 4 +--
path.c | 11 ++------
refs.c | 78 +++++++++++++++++++++++++++++++++---------------------
refs.h | 2 +-
5 files changed, 65 insertions(+), 55 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 64c2aca..efb5e2f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -585,18 +585,21 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (opts->new_orphan_branch) {
if (opts->new_branch_log && !log_all_ref_updates) {
int temp;
- char log_file[PATH_MAX];
- const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+ struct strbuf log_file = STRBUF_INIT;
+ int ret;
+ const char *ref_name;
+ ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
temp = log_all_ref_updates;
log_all_ref_updates = 1;
- if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+ ret = log_ref_setup(ref_name, &log_file);
+ log_all_ref_updates = temp;
+ strbuf_release(&log_file);
+ if (ret) {
fprintf(stderr, _("Can not do reflog for '%s'\n"),
opts->new_orphan_branch);
- log_all_ref_updates = temp;
return;
}
- log_all_ref_updates = temp;
}
}
else
@@ -651,14 +654,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
new->name);
}
}
- if (old->path && old->name) {
- char log_file[PATH_MAX], ref_file[PATH_MAX];
-
- git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
- git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
- if (!file_exists(ref_file) && file_exists(log_file))
- remove_path(log_file);
- }
+ if (old->path && old->name &&
+ !file_exists(git_path("%s", old->path)) &&
+ file_exists(git_path("logs/%s", old->path)))
+ remove_path(git_path("logs/%s", old->path));
}
remove_branch_state();
strbuf_release(&msg);
diff --git a/cache.h b/cache.h
index a344a5f..0fae730 100644
--- a/cache.h
+++ b/cache.h
@@ -646,8 +646,8 @@ extern int check_repository_format(void);
extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
-extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
- __attribute__((format (printf, 3, 4)));
+extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
extern char *git_pathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
extern char *mkpathdup(const char *fmt, ...)
diff --git a/path.c b/path.c
index 36d461e..417df76 100644
--- a/path.c
+++ b/path.c
@@ -70,19 +70,12 @@ static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
strbuf_cleanup_path(buf);
}
-char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
- struct strbuf sb = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- vsnpath(&sb, fmt, args);
+ vsnpath(sb, fmt, args);
va_end(args);
- if (sb.len >= n)
- strlcpy(buf, bad_path, n);
- else
- memcpy(buf, sb.buf, sb.len + 1);
- strbuf_release(&sb);
- return buf;
}
char *git_pathdup(const char *fmt, ...)
diff --git a/refs.c b/refs.c
index f846f2f..c5613b0 100644
--- a/refs.c
+++ b/refs.c
@@ -1325,10 +1325,12 @@ static const char *handle_missing_loose_ref(const char *refname,
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
{
+ struct strbuf sb_path = STRBUF_INIT;
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char refname_buffer[256];
+ const char *ret;
if (flag)
*flag = 0;
@@ -1337,15 +1339,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
return NULL;
for (;;) {
- char path[PATH_MAX];
+ const char *path;
struct stat st;
char *buf;
int fd;
if (--depth < 0)
- return NULL;
+ goto fail;
- git_snpath(path, sizeof(path), "%s", refname);
+ strbuf_reset(&sb_path);
+ strbuf_git_path(&sb_path, "%s", refname);
+ path = sb_path.buf;
/*
* We might have to loop back here to avoid a race
@@ -1359,10 +1363,11 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
stat_ref:
if (lstat(path, &st) < 0) {
if (errno == ENOENT)
- return handle_missing_loose_ref(refname, sha1,
- reading, flag);
+ ret = handle_missing_loose_ref(refname, sha1,
+ reading, flag);
else
- return NULL;
+ ret = NULL;
+ goto done;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
@@ -1373,7 +1378,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* inconsistent with lstat; retry */
goto stat_ref;
else
- return NULL;
+ goto fail;
}
buffer[len] = 0;
if (starts_with(buffer, "refs/") &&
@@ -1389,7 +1394,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* Is it a directory? */
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
- return NULL;
+ goto fail;
}
/*
@@ -1402,12 +1407,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/* inconsistent with lstat; retry */
goto stat_ref;
else
- return NULL;
+ goto fail;
}
+
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0)
- return NULL;
+ goto fail;
while (len && isspace(buffer[len-1]))
len--;
buffer[len] = '\0';
@@ -1424,9 +1430,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
(buffer[40] != '\0' && !isspace(buffer[40]))) {
if (flag)
*flag |= REF_ISBROKEN;
- return NULL;
+ goto fail;
}
- return refname;
+ ret = refname;
+ goto done;
}
if (flag)
*flag |= REF_ISSYMREF;
@@ -1436,10 +1443,15 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flag)
*flag |= REF_ISBROKEN;
- return NULL;
+ goto fail;
}
refname = strcpy(refname_buffer, buf);
}
+fail:
+ ret = NULL;
+done:
+ strbuf_release(&sb_path);
+ return ret;
}
char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -2717,41 +2729,41 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
-int log_ref_setup(const char *refname, char *logfile, int bufsize)
+int log_ref_setup(const char *refname, struct strbuf *logfile)
{
int logfd, oflags = O_APPEND | O_WRONLY;
- git_snpath(logfile, bufsize, "logs/%s", refname);
+ strbuf_git_path(logfile, "logs/%s", refname);
if (log_all_ref_updates &&
(starts_with(refname, "refs/heads/") ||
starts_with(refname, "refs/remotes/") ||
starts_with(refname, "refs/notes/") ||
!strcmp(refname, "HEAD"))) {
- if (safe_create_leading_directories(logfile) < 0)
+ if (safe_create_leading_directories(logfile->buf) < 0)
return error("unable to create directory for %s",
- logfile);
+ logfile->buf);
oflags |= O_CREAT;
}
- logfd = open(logfile, oflags, 0666);
+ logfd = open(logfile->buf, oflags, 0666);
if (logfd < 0) {
if (!(oflags & O_CREAT) && errno == ENOENT)
return 0;
if ((oflags & O_CREAT) && errno == EISDIR) {
- if (remove_empty_directories(logfile)) {
+ if (remove_empty_directories(logfile->buf)) {
return error("There are still logs under '%s'",
- logfile);
+ logfile->buf);
}
- logfd = open(logfile, oflags, 0666);
+ logfd = open(logfile->buf, oflags, 0666);
}
if (logfd < 0)
return error("Unable to append to %s: %s",
- logfile, strerror(errno));
+ logfile->buf, strerror(errno));
}
- adjust_shared_perm(logfile);
+ adjust_shared_perm(logfile->buf);
close(logfd);
return 0;
}
@@ -2762,20 +2774,22 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
int logfd, result, written, oflags = O_APPEND | O_WRONLY;
unsigned maxlen, len;
int msglen;
- char log_file[PATH_MAX];
+ struct strbuf sb_log_file = STRBUF_INIT;
+ const char *log_file;
char *logrec;
const char *committer;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, log_file, sizeof(log_file));
+ result = log_ref_setup(refname, &sb_log_file);
if (result)
- return result;
+ goto done;
+ log_file = sb_log_file.buf;
logfd = open(log_file, oflags);
if (logfd < 0)
- return 0;
+ goto done;
msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(0);
maxlen = strlen(committer) + msglen + 100;
@@ -2788,9 +2802,13 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
- if (close(logfd) != 0 || written != len)
- return error("Unable to append to %s", log_file);
- return 0;
+ if (close(logfd) != 0 || written != len) {
+ error("Unable to append to %s", log_file);
+ result = -1;
+ }
+done:
+ strbuf_release(&sb_log_file);
+ return result;
}
static int is_branch(const char *refname)
diff --git a/refs.h b/refs.h
index 87a1a79..783033a 100644
--- a/refs.h
+++ b/refs.h
@@ -166,7 +166,7 @@ extern void unlock_ref(struct ref_lock *lock);
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
/** Setup reflog before using. **/
-int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+int log_ref_setup(const char *ref_name, struct strbuf *logfile);
/** Reads log for the value of ref during at_time. **/
extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v5 03/28] Convert git_snpath() to strbuf_git_path()
2014-03-08 2:47 ` [PATCH v5 03/28] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-17 18:11 ` Junio C Hamano
0 siblings, 0 replies; 169+ messages in thread
From: Junio C Hamano @ 2014-03-17 18:11 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> In the previous patch, git_snpath() is modified to allocate a new
> strbuf buffer because vsnpath() needs that. But that makes it awkward
> because git_snpath() receives a pre-allocated buffer from outside and
> has to copy data back. Rename it to strbuf_git_path() and make it
> receive strbuf directly.
>
> The conversion from git_snpath() to git_path() in
> update_refs_for_switch() is safe because that function does not keep
> any pointer to the round-robin buffer pool allocated by
> get_pathname().
s/that function does not/that function and all of its callers do not/
is the guarantee we need to say it is safe. And I think that holds
true.
Thanks.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> builtin/checkout.c | 25 +++++++++--------
> cache.h | 4 +--
> path.c | 11 ++------
> refs.c | 78 +++++++++++++++++++++++++++++++++---------------------
> refs.h | 2 +-
> 5 files changed, 65 insertions(+), 55 deletions(-)
>
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 64c2aca..efb5e2f 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -585,18 +585,21 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
> if (opts->new_orphan_branch) {
> if (opts->new_branch_log && !log_all_ref_updates) {
> int temp;
> - char log_file[PATH_MAX];
> - const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
> + struct strbuf log_file = STRBUF_INIT;
> + int ret;
> + const char *ref_name;
>
> + ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
> temp = log_all_ref_updates;
> log_all_ref_updates = 1;
> - if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
> + ret = log_ref_setup(ref_name, &log_file);
> + log_all_ref_updates = temp;
> + strbuf_release(&log_file);
> + if (ret) {
> fprintf(stderr, _("Can not do reflog for '%s'\n"),
> opts->new_orphan_branch);
> - log_all_ref_updates = temp;
> return;
> }
> - log_all_ref_updates = temp;
> }
> }
> else
> @@ -651,14 +654,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
> new->name);
> }
> }
> - if (old->path && old->name) {
> - char log_file[PATH_MAX], ref_file[PATH_MAX];
> -
> - git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
> - git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
> - if (!file_exists(ref_file) && file_exists(log_file))
> - remove_path(log_file);
> - }
> + if (old->path && old->name &&
> + !file_exists(git_path("%s", old->path)) &&
> + file_exists(git_path("logs/%s", old->path)))
> + remove_path(git_path("logs/%s", old->path));
> }
> remove_branch_state();
> strbuf_release(&msg);
> diff --git a/cache.h b/cache.h
> index a344a5f..0fae730 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -646,8 +646,8 @@ extern int check_repository_format(void);
>
> extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
> __attribute__((format (printf, 3, 4)));
> -extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
> - __attribute__((format (printf, 3, 4)));
> +extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
> + __attribute__((format (printf, 2, 3)));
> extern char *git_pathdup(const char *fmt, ...)
> __attribute__((format (printf, 1, 2)));
> extern char *mkpathdup(const char *fmt, ...)
> diff --git a/path.c b/path.c
> index 36d461e..417df76 100644
> --- a/path.c
> +++ b/path.c
> @@ -70,19 +70,12 @@ static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
> strbuf_cleanup_path(buf);
> }
>
> -char *git_snpath(char *buf, size_t n, const char *fmt, ...)
> +void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
> {
> - struct strbuf sb = STRBUF_INIT;
> va_list args;
> va_start(args, fmt);
> - vsnpath(&sb, fmt, args);
> + vsnpath(sb, fmt, args);
> va_end(args);
> - if (sb.len >= n)
> - strlcpy(buf, bad_path, n);
> - else
> - memcpy(buf, sb.buf, sb.len + 1);
> - strbuf_release(&sb);
> - return buf;
> }
>
> char *git_pathdup(const char *fmt, ...)
> diff --git a/refs.c b/refs.c
> index f846f2f..c5613b0 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1325,10 +1325,12 @@ static const char *handle_missing_loose_ref(const char *refname,
>
> const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
> {
> + struct strbuf sb_path = STRBUF_INIT;
> int depth = MAXDEPTH;
> ssize_t len;
> char buffer[256];
> static char refname_buffer[256];
> + const char *ret;
>
> if (flag)
> *flag = 0;
> @@ -1337,15 +1339,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> return NULL;
>
> for (;;) {
> - char path[PATH_MAX];
> + const char *path;
> struct stat st;
> char *buf;
> int fd;
>
> if (--depth < 0)
> - return NULL;
> + goto fail;
>
> - git_snpath(path, sizeof(path), "%s", refname);
> + strbuf_reset(&sb_path);
> + strbuf_git_path(&sb_path, "%s", refname);
> + path = sb_path.buf;
>
> /*
> * We might have to loop back here to avoid a race
> @@ -1359,10 +1363,11 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> stat_ref:
> if (lstat(path, &st) < 0) {
> if (errno == ENOENT)
> - return handle_missing_loose_ref(refname, sha1,
> - reading, flag);
> + ret = handle_missing_loose_ref(refname, sha1,
> + reading, flag);
> else
> - return NULL;
> + ret = NULL;
> + goto done;
> }
>
> /* Follow "normalized" - ie "refs/.." symlinks by hand */
> @@ -1373,7 +1378,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> /* inconsistent with lstat; retry */
> goto stat_ref;
> else
> - return NULL;
> + goto fail;
> }
> buffer[len] = 0;
> if (starts_with(buffer, "refs/") &&
> @@ -1389,7 +1394,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> /* Is it a directory? */
> if (S_ISDIR(st.st_mode)) {
> errno = EISDIR;
> - return NULL;
> + goto fail;
> }
>
> /*
> @@ -1402,12 +1407,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> /* inconsistent with lstat; retry */
> goto stat_ref;
> else
> - return NULL;
> + goto fail;
> }
> +
> len = read_in_full(fd, buffer, sizeof(buffer)-1);
> close(fd);
> if (len < 0)
> - return NULL;
> + goto fail;
> while (len && isspace(buffer[len-1]))
> len--;
> buffer[len] = '\0';
> @@ -1424,9 +1430,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> (buffer[40] != '\0' && !isspace(buffer[40]))) {
> if (flag)
> *flag |= REF_ISBROKEN;
> - return NULL;
> + goto fail;
> }
> - return refname;
> + ret = refname;
> + goto done;
> }
> if (flag)
> *flag |= REF_ISSYMREF;
> @@ -1436,10 +1443,15 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
> if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
> if (flag)
> *flag |= REF_ISBROKEN;
> - return NULL;
> + goto fail;
> }
> refname = strcpy(refname_buffer, buf);
> }
> +fail:
> + ret = NULL;
> +done:
> + strbuf_release(&sb_path);
> + return ret;
> }
>
> char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
> @@ -2717,41 +2729,41 @@ static int copy_msg(char *buf, const char *msg)
> return cp - buf;
> }
>
> -int log_ref_setup(const char *refname, char *logfile, int bufsize)
> +int log_ref_setup(const char *refname, struct strbuf *logfile)
> {
> int logfd, oflags = O_APPEND | O_WRONLY;
>
> - git_snpath(logfile, bufsize, "logs/%s", refname);
> + strbuf_git_path(logfile, "logs/%s", refname);
> if (log_all_ref_updates &&
> (starts_with(refname, "refs/heads/") ||
> starts_with(refname, "refs/remotes/") ||
> starts_with(refname, "refs/notes/") ||
> !strcmp(refname, "HEAD"))) {
> - if (safe_create_leading_directories(logfile) < 0)
> + if (safe_create_leading_directories(logfile->buf) < 0)
> return error("unable to create directory for %s",
> - logfile);
> + logfile->buf);
> oflags |= O_CREAT;
> }
>
> - logfd = open(logfile, oflags, 0666);
> + logfd = open(logfile->buf, oflags, 0666);
> if (logfd < 0) {
> if (!(oflags & O_CREAT) && errno == ENOENT)
> return 0;
>
> if ((oflags & O_CREAT) && errno == EISDIR) {
> - if (remove_empty_directories(logfile)) {
> + if (remove_empty_directories(logfile->buf)) {
> return error("There are still logs under '%s'",
> - logfile);
> + logfile->buf);
> }
> - logfd = open(logfile, oflags, 0666);
> + logfd = open(logfile->buf, oflags, 0666);
> }
>
> if (logfd < 0)
> return error("Unable to append to %s: %s",
> - logfile, strerror(errno));
> + logfile->buf, strerror(errno));
> }
>
> - adjust_shared_perm(logfile);
> + adjust_shared_perm(logfile->buf);
> close(logfd);
> return 0;
> }
> @@ -2762,20 +2774,22 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
> int logfd, result, written, oflags = O_APPEND | O_WRONLY;
> unsigned maxlen, len;
> int msglen;
> - char log_file[PATH_MAX];
> + struct strbuf sb_log_file = STRBUF_INIT;
> + const char *log_file;
> char *logrec;
> const char *committer;
>
> if (log_all_ref_updates < 0)
> log_all_ref_updates = !is_bare_repository();
>
> - result = log_ref_setup(refname, log_file, sizeof(log_file));
> + result = log_ref_setup(refname, &sb_log_file);
> if (result)
> - return result;
> + goto done;
> + log_file = sb_log_file.buf;
>
> logfd = open(log_file, oflags);
> if (logfd < 0)
> - return 0;
> + goto done;
> msglen = msg ? strlen(msg) : 0;
> committer = git_committer_info(0);
> maxlen = strlen(committer) + msglen + 100;
> @@ -2788,9 +2802,13 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
> len += copy_msg(logrec + len - 1, msg) - 1;
> written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
> free(logrec);
> - if (close(logfd) != 0 || written != len)
> - return error("Unable to append to %s", log_file);
> - return 0;
> + if (close(logfd) != 0 || written != len) {
> + error("Unable to append to %s", log_file);
> + result = -1;
> + }
> +done:
> + strbuf_release(&sb_log_file);
> + return result;
> }
>
> static int is_branch(const char *refname)
> diff --git a/refs.h b/refs.h
> index 87a1a79..783033a 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -166,7 +166,7 @@ extern void unlock_ref(struct ref_lock *lock);
> extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
>
> /** Setup reflog before using. **/
> -int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
> +int log_ref_setup(const char *ref_name, struct strbuf *logfile);
>
> /** Reads log for the value of ref during at_time. **/
> extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v5 04/28] path.c: rename vsnpath() to do_git_path()
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (2 preceding siblings ...)
2014-03-08 2:47 ` [PATCH v5 03/28] Convert git_snpath() to strbuf_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 05/28] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
` (25 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The name vsnpath() gives an impression that this is general path
handling function. It's not. This is the underlying implementation of
git_path(), git_pathdup() and strbuf_git_path() which will prefix
$GIT_DIR in the result string.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/path.c b/path.c
index 417df76..987fdec 100644
--- a/path.c
+++ b/path.c
@@ -60,7 +60,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
-static void vsnpath(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
{
const char *git_dir = get_git_dir();
strbuf_addstr(buf, git_dir);
@@ -74,7 +74,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- vsnpath(sb, fmt, args);
+ do_git_path(sb, fmt, args);
va_end(args);
}
@@ -83,7 +83,7 @@ char *git_pathdup(const char *fmt, ...)
struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- vsnpath(&path, fmt, args);
+ do_git_path(&path, fmt, args);
va_end(args);
return strbuf_detach(&path, NULL);
}
@@ -114,7 +114,7 @@ const char *git_path(const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- vsnpath(pathname, fmt, args);
+ do_git_path(pathname, fmt, args);
va_end(args);
return pathname->buf;
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 05/28] path.c: group git_path(), git_pathdup() and strbuf_git_path() together
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (3 preceding siblings ...)
2014-03-08 2:47 ` [PATCH v5 04/28] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:47 ` [PATCH v5 06/28] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
` (24 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
path.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/path.c b/path.c
index 987fdec..1c0f160 100644
--- a/path.c
+++ b/path.c
@@ -78,6 +78,16 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
va_end(args);
}
+const char *git_path(const char *fmt, ...)
+{
+ struct strbuf *pathname = get_pathname();
+ va_list args;
+ va_start(args, fmt);
+ do_git_path(pathname, fmt, args);
+ va_end(args);
+ return pathname->buf;
+}
+
char *git_pathdup(const char *fmt, ...)
{
struct strbuf path = STRBUF_INIT;
@@ -109,16 +119,6 @@ const char *mkpath(const char *fmt, ...)
return cleanup_path(pathname->buf);
}
-const char *git_path(const char *fmt, ...)
-{
- struct strbuf *pathname = get_pathname();
- va_list args;
- va_start(args, fmt);
- do_git_path(pathname, fmt, args);
- va_end(args);
- return pathname->buf;
-}
-
void home_config_paths(char **global, char **xdg, char *file)
{
char *xdg_home = getenv("XDG_CONFIG_HOME");
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 06/28] Make git_path() aware of file relocation in $GIT_DIR
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (4 preceding siblings ...)
2014-03-08 2:47 ` [PATCH v5 05/28] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-09 8:19 ` Eric Sunshine
2014-03-08 2:47 ` [PATCH v5 07/28] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
` (23 subsequent siblings)
29 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
We allow the user to relocate certain paths out of $GIT_DIR via
environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and
GIT_GRAFT_FILE. Callers are not supposed to use git_path() or
git_pathdup() to get those paths. Instead they must use
get_object_directory(), get_index_file() and get_graft_file()
respectively. This is inconvenient and could be missed in review (for
example, there's git_path("objects/info/alternates") somewhere in
sha1_file.c).
This patch makes git_path() and git_pathdup() understand those
environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar,
git_path("objects/abc") should return /foo/bar/abc. The same is done
for the two remaining env variables.
"git rev-parse --git-path" is the wrapper for script use.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-rev-parse.txt | 7 ++++++
builtin/rev-parse.c | 7 ++++++
cache.h | 1 +
environment.c | 9 ++++++--
path.c | 49 +++++++++++++++++++++++++++++++++++++++--
t/t0060-path-utils.sh | 19 ++++++++++++++++
6 files changed, 88 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 0d2cdcd..46020d9 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -232,6 +232,13 @@ print a message to stderr and exit with nonzero status.
repository. If <path> is a gitfile then the resolved path
to the real repository is printed.
+--git-path <path>::
+ Resolve "$GIT_DIR/<path>" and takes other path relocation
+ variables such as $GIT_OBJECT_DIRECTORY,
+ $GIT_INDEX_FILE... into account. For example, if
+ $GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
+ --git-path objects/abc" returns /tmp/bar/abc.
+
--show-cdup::
When the command is invoked from a subdirectory, show the
path of the top-level directory relative to the current
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index aaeb611..e50bc65 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -518,6 +518,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
+ if (!strcmp(arg, "--git-path")) {
+ if (!argv[i + 1])
+ die("--git-path requires an argument");
+ puts(git_path("%s", argv[i + 1]));
+ i++;
+ continue;
+ }
if (as_is) {
if (show_file(arg, output_prefix) && as_is < 2)
verify_filename(prefix, arg, 0);
diff --git a/cache.h b/cache.h
index 0fae730..0f5f44f 100644
--- a/cache.h
+++ b/cache.h
@@ -585,6 +585,7 @@ extern int fsync_object_files;
extern int core_preload_index;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
+extern int git_db_env, git_index_env, git_graft_env;
/*
* The character that begins a commented line in user-editable file
diff --git a/environment.c b/environment.c
index 4a3437d..f513479 100644
--- a/environment.c
+++ b/environment.c
@@ -82,6 +82,7 @@ static size_t namespace_len;
static const char *git_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
+int git_db_env, git_index_env, git_graft_env;
/*
* Repository-local GIT_* environment variables; see cache.h for details.
@@ -137,15 +138,19 @@ static void setup_git_env(void)
if (!git_object_dir) {
git_object_dir = xmalloc(strlen(git_dir) + 9);
sprintf(git_object_dir, "%s/objects", git_dir);
- }
+ } else
+ git_db_env = 1;
git_index_file = getenv(INDEX_ENVIRONMENT);
if (!git_index_file) {
git_index_file = xmalloc(strlen(git_dir) + 7);
sprintf(git_index_file, "%s/index", git_dir);
- }
+ } else
+ git_index_env = 1;
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
git_graft_file = git_pathdup("info/grafts");
+ else
+ git_graft_env = 1;
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
read_replace_refs = 0;
namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
diff --git a/path.c b/path.c
index 1c0f160..1069ae0 100644
--- a/path.c
+++ b/path.c
@@ -60,13 +60,58 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
return cleanup_path(buf);
}
+static int dir_prefix(const char *buf, const char *dir)
+{
+ int len = strlen(dir);
+ return !strncmp(buf, dir, len) &&
+ (is_dir_sep(buf[len]) || buf[len] == '\0');
+}
+
+/* $buf =~ m|$dir/+$file| but without regex */
+static int is_dir_file(const char *buf, const char *dir, const char *file)
+{
+ int len = strlen(dir);
+ if (strncmp(buf, dir, len) || !is_dir_sep(buf[len]))
+ return 0;
+ while (is_dir_sep(buf[len]))
+ len++;
+ return !strcmp(buf + len, file);
+}
+
+static void replace_dir(struct strbuf *buf, int len, const char *newdir)
+{
+ int newlen = strlen(newdir);
+ int need_sep = (buf->buf[len] && !is_dir_sep(buf->buf[len])) &&
+ !is_dir_sep(newdir[newlen - 1]);
+ if (need_sep)
+ len--; /* keep one char, to be replaced with '/' */
+ strbuf_splice(buf, 0, len, newdir, newlen);
+ if (need_sep)
+ buf->buf[newlen] = '/';
+}
+
+static void adjust_git_path(struct strbuf *buf, int git_dir_len)
+{
+ const char *base = buf->buf + git_dir_len;
+ if (git_graft_env && is_dir_file(base, "info", "grafts"))
+ strbuf_splice(buf, 0, buf->len,
+ get_graft_file(), strlen(get_graft_file()));
+ else if (git_index_env && !strcmp(base, "index"))
+ strbuf_splice(buf, 0, buf->len,
+ get_index_file(), strlen(get_index_file()));
+ else if (git_db_env && dir_prefix(base, "objects"))
+ replace_dir(buf, git_dir_len + 7, get_object_directory());
+}
+
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
{
- const char *git_dir = get_git_dir();
- strbuf_addstr(buf, git_dir);
+ int gitdir_len;
+ strbuf_addstr(buf, get_git_dir());
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
+ gitdir_len = buf->len;
strbuf_vaddf(buf, fmt, args);
+ adjust_git_path(buf, gitdir_len);
strbuf_cleanup_path(buf);
}
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 07c10c8..1d29901 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -19,6 +19,14 @@ relative_path() {
"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
}
+test_git_path() {
+ test_expect_success "git-path $1 $2 => $3" "
+ $1 git rev-parse --git-path $2 >actual &&
+ echo $3 >expect &&
+ test_cmp expect actual
+ "
+}
+
# On Windows, we are using MSYS's bash, which mangles the paths.
# Absolute paths are anchored at the MSYS installation directory,
# which means that the path / accounts for this many characters:
@@ -223,4 +231,15 @@ relative_path "<null>" "<empty>" ./
relative_path "<null>" "<null>" ./
relative_path "<null>" /foo/a/b ./
+test_git_path A=B info/grafts .git/info/grafts
+test_git_path GIT_GRAFT_FILE=foo info/grafts foo
+test_git_path GIT_GRAFT_FILE=foo info/////grafts foo
+test_git_path GIT_INDEX_FILE=foo index foo
+test_git_path GIT_INDEX_FILE=foo index/foo .git/index/foo
+test_git_path GIT_INDEX_FILE=foo index2 .git/index2
+test_expect_success 'setup fake objects directory foo' 'mkdir foo'
+test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v5 06/28] Make git_path() aware of file relocation in $GIT_DIR
2014-03-08 2:47 ` [PATCH v5 06/28] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-09 8:19 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-09 8:19 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Fri, Mar 7, 2014 at 9:47 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> We allow the user to relocate certain paths out of $GIT_DIR via
> environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and
> GIT_GRAFT_FILE. Callers are not supposed to use git_path() or
> git_pathdup() to get those paths. Instead they must use
> get_object_directory(), get_index_file() and get_graft_file()
> respectively. This is inconvenient and could be missed in review (for
> example, there's git_path("objects/info/alternates") somewhere in
> sha1_file.c).
>
> This patch makes git_path() and git_pathdup() understand those
> environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar,
> git_path("objects/abc") should return /foo/bar/abc. The same is done
> for the two remaining env variables.
>
> "git rev-parse --git-path" is the wrapper for script use.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> Documentation/git-rev-parse.txt | 7 ++++++
> builtin/rev-parse.c | 7 ++++++
> cache.h | 1 +
> environment.c | 9 ++++++--
> path.c | 49 +++++++++++++++++++++++++++++++++++++++--
> t/t0060-path-utils.sh | 19 ++++++++++++++++
> 6 files changed, 88 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
> index 0d2cdcd..46020d9 100644
> --- a/Documentation/git-rev-parse.txt
> +++ b/Documentation/git-rev-parse.txt
> @@ -232,6 +232,13 @@ print a message to stderr and exit with nonzero status.
> repository. If <path> is a gitfile then the resolved path
> to the real repository is printed.
>
> +--git-path <path>::
> + Resolve "$GIT_DIR/<path>" and takes other path relocation
> + variables such as $GIT_OBJECT_DIRECTORY,
> + $GIT_INDEX_FILE... into account. For example, if
> + $GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
> + --git-path objects/abc" returns /tmp/bar/abc.
s/tmp/foo/
> +
> --show-cdup::
> When the command is invoked from a subdirectory, show the
> path of the top-level directory relative to the current
> diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
> index aaeb611..e50bc65 100644
> --- a/builtin/rev-parse.c
> +++ b/builtin/rev-parse.c
> @@ -518,6 +518,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
> for (i = 1; i < argc; i++) {
> const char *arg = argv[i];
>
> + if (!strcmp(arg, "--git-path")) {
> + if (!argv[i + 1])
> + die("--git-path requires an argument");
> + puts(git_path("%s", argv[i + 1]));
> + i++;
> + continue;
> + }
> if (as_is) {
> if (show_file(arg, output_prefix) && as_is < 2)
> verify_filename(prefix, arg, 0);
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v5 07/28] *.sh: respect $GIT_INDEX_FILE
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (5 preceding siblings ...)
2014-03-08 2:47 ` [PATCH v5 06/28] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:47 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 08/28] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
` (22 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:47 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-pull.sh | 2 +-
git-stash.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-pull.sh b/git-pull.sh
index 0a5aa2c..2a90262 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -218,7 +218,7 @@ test true = "$rebase" && {
if ! git rev-parse -q --verify HEAD >/dev/null
then
# On an unborn branch
- if test -f "$GIT_DIR/index"
+ if test -f "$(git rev-parse --git-path index)"
then
die "$(gettext "updating an unborn branch with changes added to the index")"
fi
diff --git a/git-stash.sh b/git-stash.sh
index f0a94ab..0de9a6c 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -20,7 +20,7 @@ require_work_tree
cd_to_toplevel
TMP="$GIT_DIR/.git-stash.$$"
-TMPindex=${GIT_INDEX_FILE-"$GIT_DIR/index"}.stash.$$
+TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
trap 'rm -f "$TMP-"* "$TMPindex"' 0
ref_stash=refs/stash
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 08/28] reflog: avoid constructing .lock path with git_path
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (6 preceding siblings ...)
2014-03-08 2:47 ` [PATCH v5 07/28] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 09/28] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
` (21 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Among pathnames in $GIT_DIR, e.g. "index" or "packed-refs", we want to
automatically and silently map some of them to the $GIT_DIR of the
repository we are borrowing from via $GIT_COMMON_DIR mechanism. When
we formulate the pathname for its lockfile, we want it to be in the
same location as its final destination. "index" is not shared and
needs to remain in the borrowing repository, while "packed-refs" is
shared and needs to go to the borrowed repository.
git_path() could be taught about the ".lock" suffix and map
"index.lock" and "packed-refs.lock" the same way their basenames are
mapped, but instead the caller can help by asking where the basename
(e.g. "index") is mapped to git_path() and then appending ".lock"
after the mapping is done.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/reflog.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 852cff6..ccf2cf6 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -372,7 +372,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
if (!file_exists(log_file))
goto finish;
if (!cmd->dry_run) {
- newlog_path = git_pathdup("logs/%s.lock", ref);
+ newlog_path = mkpathdup("%s.lock", log_file);
cb.newlog = fopen(newlog_path, "w");
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 09/28] fast-import: use git_path() for accessing .git dir instead of get_git_dir()
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (7 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 08/28] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 10/28] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
` (20 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This allows git_path() to redirect info/fast-import to another place
if needed
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
fast-import.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/fast-import.c b/fast-import.c
index a9f328d..28e7a63 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -3125,12 +3125,9 @@ static void parse_progress(void)
static char* make_fast_import_path(const char *path)
{
- struct strbuf abs_path = STRBUF_INIT;
-
if (!relative_marks_paths || is_absolute_path(path))
return xstrdup(path);
- strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
- return strbuf_detach(&abs_path, NULL);
+ return xstrdup(git_path("info/fast-import/%s", path));
}
static void option_import_marks(const char *marks,
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 10/28] commit: use SEQ_DIR instead of hardcoding "sequencer"
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (8 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 09/28] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 11/28] Add new environment variable $GIT_COMMON_DIR Nguyễn Thái Ngọc Duy
` (19 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/commit.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/commit.c b/builtin/commit.c
index 3767478..ee3ac10 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -155,7 +155,7 @@ static void determine_whence(struct wt_status *s)
whence = FROM_MERGE;
else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
whence = FROM_CHERRY_PICK;
- if (file_exists(git_path("sequencer")))
+ if (file_exists(git_path(SEQ_DIR)))
sequencer_in_use = 1;
}
else
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 11/28] Add new environment variable $GIT_COMMON_DIR
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (9 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 10/28] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 12/28] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
` (18 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This variable is intended to support multiple working directories
attached to a repository. Such a repository may have a main working
directory, created by either "git init" or "git clone" and one or more
linked working directories. These working directories and the main
repository share the same repository directory.
In linked working directories, $GIT_COMMON_DIR must be defined to point
to the real repository directory and $GIT_DIR points to an unused
subdirectory inside $GIT_COMMON_DIR. File locations inside the
repository are reorganized from the linked worktree view point:
- worktree-specific such as HEAD, logs/HEAD, index, other top-level
refs and unrecognized files are from $GIT_DIR.
- the rest like objects, refs, info, hooks, packed-refs, shallow...
are from $GIT_COMMON_DIR
Scripts are supposed to retrieve paths in $GIT_DIR with "git rev-parse
--git-path", which will take care of "$GIT_DIR vs $GIT_COMMON_DIR"
business.
The redirection is done by git_path(), git_pathdup() and
strbuf_git_path(). The selected list of paths goes to $GIT_COMMON_DIR,
not the other way around in case a developer adds a new
worktree-specific file and it's accidentally promoted to be shared
across repositories (this includes unknown files added by third party
commands)
The list of known files that belong to $GIT_DIR are:
ADD_EDIT.patch BISECT_ANCESTORS_OK BISECT_EXPECTED_REV BISECT_LOG
BISECT_NAMES CHERRY_PICK_HEAD COMMIT_MSG FETCH_HEAD HEAD MERGE_HEAD
MERGE_MODE MERGE_RR NOTES_EDITMSG NOTES_MERGE_WORKTREE ORIG_HEAD
REVERT_HEAD SQUASH_MSG TAG_EDITMSG fast_import_crash_* logs/HEAD
next-index-* rebase-apply rebase-merge rsync-refs-* sequencer/*
shallow_*
Path mapping is NOT done for git_path_submodule(). Multi-checkouts are
not supported as submodules.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git.txt | 8 +++++++
Documentation/gitrepository-layout.txt | 42 ++++++++++++++++++++++++++--------
cache.h | 4 +++-
environment.c | 19 +++++++++++----
path.c | 34 +++++++++++++++++++++++++++
t/t0060-path-utils.sh | 15 ++++++++++++
6 files changed, 108 insertions(+), 14 deletions(-)
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 02bbc08..b094b1f 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -773,6 +773,14 @@ Git so take care if using Cogito etc.
an explicit repository directory set via 'GIT_DIR' or on the
command line.
+'GIT_COMMON_DIR'::
+ If this variable is set to a path, non-worktree files that are
+ normally in $GIT_DIR will be taken from this path
+ instead. Worktree-specific files such as HEAD or index are
+ taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for
+ details. This variable has lower precedence than other path
+ variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
+
Git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index aa03882..556da09 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -46,6 +46,9 @@ of incomplete object store is not suitable to be published for
use with dumb transports but otherwise is OK as long as
`objects/info/alternates` points at the object stores it
borrows from.
++
+This directory is ignored if $GIT_COMMON_DIR is set and
+"$GIT_COMMON_DIR/objects" will be used instead.
objects/[0-9a-f][0-9a-f]::
A newly created object is stored in its own file.
@@ -92,7 +95,8 @@ refs::
References are stored in subdirectories of this
directory. The 'git prune' command knows to preserve
objects reachable from refs found in this directory and
- its subdirectories.
+ its subdirectories. This directory is ignored if $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/refs" will be used instead.
refs/heads/`name`::
records tip-of-the-tree commit objects of branch `name`
@@ -114,7 +118,8 @@ refs/replace/`<obj-sha1>`::
packed-refs::
records the same information as refs/heads/, refs/tags/,
and friends record in a more efficient way. See
- linkgit:git-pack-refs[1].
+ linkgit:git-pack-refs[1]. This file is ignored if $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/packed-refs" will be used instead.
HEAD::
A symref (see glossary) to the `refs/heads/` namespace
@@ -133,6 +138,11 @@ being a symref to point at the current branch. Such a state
is often called 'detached HEAD.' See linkgit:git-checkout[1]
for details.
+config::
+ Repository specific configuration file. This file is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
+ used instead.
+
branches::
A slightly deprecated way to store shorthands to be used
to specify a URL to 'git fetch', 'git pull' and 'git push'.
@@ -140,7 +150,10 @@ branches::
'name' can be given to these commands in place of
'repository' argument. See the REMOTES section in
linkgit:git-fetch[1] for details. This mechanism is legacy
- and not likely to be found in modern repositories.
+ and not likely to be found in modern repositories. This
+ directory is ignored if $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/branches" will be used instead.
+
hooks::
Hooks are customization scripts used by various Git
@@ -149,7 +162,9 @@ hooks::
default. To enable, the `.sample` suffix has to be
removed from the filename by renaming.
Read linkgit:githooks[5] for more details about
- each hook.
+ each hook. This directory is ignored if $GIT_COMMON_DIR is set
+ and "$GIT_COMMON_DIR/hooks" will be used instead.
+
index::
The current index file for the repository. It is
@@ -157,7 +172,8 @@ index::
info::
Additional information about the repository is recorded
- in this directory.
+ in this directory. This directory is ignored if $GIT_COMMON_DIR
+ is set and "$GIT_COMMON_DIR/index" will be used instead.
info/refs::
This file helps dumb transports discover what refs are
@@ -193,12 +209,16 @@ remotes::
when interacting with remote repositories via 'git fetch',
'git pull' and 'git push' commands. See the REMOTES section
in linkgit:git-fetch[1] for details. This mechanism is legacy
- and not likely to be found in modern repositories.
+ and not likely to be found in modern repositories. This
+ directory is ignored if $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/remotes" will be used instead.
logs::
Records of changes made to refs are stored in this
directory. See linkgit:git-update-ref[1]
- for more information.
+ for more information. This directory is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/logs" will be used
+ instead.
logs/refs/heads/`name`::
Records all changes made to the branch tip named `name`.
@@ -209,10 +229,14 @@ logs/refs/tags/`name`::
shallow::
This is similar to `info/grafts` but is internally used
and maintained by shallow clone mechanism. See `--depth`
- option to linkgit:git-clone[1] and linkgit:git-fetch[1].
+ option to linkgit:git-clone[1] and linkgit:git-fetch[1]. This
+ file is ignored if $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/shallow" will be used instead.
modules::
- Contains the git-repositories of the submodules.
+ Contains the git-repositories of the submodules. This
+ directory is ignored if $GIT_COMMON_DIR is set and
+ "$GIT_COMMON_DIR/modules" will be used instead.
SEE ALSO
--------
diff --git a/cache.h b/cache.h
index 0f5f44f..9182427 100644
--- a/cache.h
+++ b/cache.h
@@ -347,6 +347,7 @@ static inline enum object_type object_type(unsigned int mode)
/* Double-check local_repo_env below if you add to this list. */
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
@@ -400,6 +401,7 @@ extern int is_inside_git_dir(void);
extern char *git_work_tree_cfg;
extern int is_inside_work_tree(void);
extern const char *get_git_dir(void);
+extern const char *get_git_common_dir(void);
extern int is_git_directory(const char *path);
extern char *get_object_directory(void);
extern char *get_index_file(void);
@@ -585,7 +587,7 @@ extern int fsync_object_files;
extern int core_preload_index;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
-extern int git_db_env, git_index_env, git_graft_env;
+extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
/*
* The character that begins a commented line in user-editable file
diff --git a/environment.c b/environment.c
index f513479..c998120 100644
--- a/environment.c
+++ b/environment.c
@@ -80,9 +80,9 @@ static char *work_tree;
static const char *namespace;
static size_t namespace_len;
-static const char *git_dir;
+static const char *git_dir, *git_common_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
-int git_db_env, git_index_env, git_graft_env;
+int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
/*
* Repository-local GIT_* environment variables; see cache.h for details.
@@ -134,10 +134,16 @@ static void setup_git_env(void)
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
gitfile = read_gitfile(git_dir);
git_dir = xstrdup(gitfile ? gitfile : git_dir);
+ git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (git_common_dir) {
+ git_common_dir_env = 1;
+ git_common_dir = xstrdup(git_common_dir);
+ } else
+ git_common_dir = git_dir;
git_object_dir = getenv(DB_ENVIRONMENT);
if (!git_object_dir) {
- git_object_dir = xmalloc(strlen(git_dir) + 9);
- sprintf(git_object_dir, "%s/objects", git_dir);
+ git_object_dir = xmalloc(strlen(git_common_dir) + 9);
+ sprintf(git_object_dir, "%s/objects", git_common_dir);
} else
git_db_env = 1;
git_index_file = getenv(INDEX_ENVIRONMENT);
@@ -173,6 +179,11 @@ const char *get_git_dir(void)
return git_dir;
}
+const char *get_git_common_dir(void)
+{
+ return git_common_dir;
+}
+
const char *get_git_namespace(void)
{
if (!namespace)
diff --git a/path.c b/path.c
index 1069ae0..8453687 100644
--- a/path.c
+++ b/path.c
@@ -90,6 +90,38 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
buf->buf[newlen] = '/';
}
+static const char *common_list[] = {
+ "/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules",
+ "/objects", "/refs", "/remotes", "/rr-cache", "/svn",
+ "config", "gc.pid", "packed-refs", "shallow",
+ NULL
+};
+
+static void update_common_dir(struct strbuf *buf, int git_dir_len)
+{
+ char *base = buf->buf + git_dir_len;
+ const char **p;
+
+ if (is_dir_file(base, "logs", "HEAD"))
+ return; /* keep this in $GIT_DIR */
+ for (p = common_list; *p; p++) {
+ const char *path = *p;
+ int is_dir = 0;
+ if (*path == '/') {
+ path++;
+ is_dir = 1;
+ }
+ if (is_dir && dir_prefix(base, path)) {
+ replace_dir(buf, git_dir_len, get_git_common_dir());
+ return;
+ }
+ if (!is_dir && !strcmp(base, path)) {
+ replace_dir(buf, git_dir_len, get_git_common_dir());
+ return;
+ }
+ }
+}
+
static void adjust_git_path(struct strbuf *buf, int git_dir_len)
{
const char *base = buf->buf + git_dir_len;
@@ -101,6 +133,8 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
get_index_file(), strlen(get_index_file()));
else if (git_db_env && dir_prefix(base, "objects"))
replace_dir(buf, git_dir_len + 7, get_object_directory());
+ else if (git_common_dir_env)
+ update_common_dir(buf, git_dir_len);
}
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 1d29901..f9a77e4 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -241,5 +241,20 @@ test_expect_success 'setup fake objects directory foo' 'mkdir foo'
test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+test_expect_success 'setup common repository' 'git --git-dir=bar init'
+test_git_path GIT_COMMON_DIR=bar index .git/index
+test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar objects bar/objects
+test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar
+test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude
+test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar
+test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar
+test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me
+test_git_path GIT_COMMON_DIR=bar config bar/config
+test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
+test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 12/28] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (10 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 11/28] Add new environment variable $GIT_COMMON_DIR Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 13/28] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
` (17 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If $GIT_COMMON_DIR is set, $GIT_OBJECT_DIRECTORY should be
$GIT_COMMON_DIR/objects, not $GIT_DIR/objects. Just let rev-parse
--git-path handle it.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-sh-setup.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index fffa3c7..475ca43 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -343,7 +343,7 @@ then
echo >&2 "Unable to determine absolute path of git directory"
exit 1
}
- : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+ : ${GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)"}
fi
peel_committish () {
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 13/28] *.sh: avoid hardcoding $GIT_DIR/hooks/...
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (11 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 12/28] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 14/28] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
` (16 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If $GIT_COMMON_DIR is set, it should be $GIT_COMMON_DIR/hooks/, not
$GIT_DIR/hooks/. Just let rev-parse --git-path handle it.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-am.sh | 22 +++++++++++-----------
git-rebase--interactive.sh | 6 +++---
git-rebase--merge.sh | 6 ++----
git-rebase.sh | 4 ++--
templates/hooks--applypatch-msg.sample | 4 ++--
templates/hooks--pre-applypatch.sample | 4 ++--
6 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/git-am.sh b/git-am.sh
index bbea430..10babd2 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -803,10 +803,10 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
continue
fi
- if test -x "$GIT_DIR"/hooks/applypatch-msg
+ hook="$(git rev-parse --git-path hooks/applypatch-msg)"
+ if test -x "$hook"
then
- "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
- stop_here $this
+ "$hook" "$dotest/final-commit" || stop_here $this
fi
if test -f "$dotest/final-commit"
@@ -880,9 +880,10 @@ did you forget to use 'git add'?"
stop_here_user_resolve $this
fi
- if test -x "$GIT_DIR"/hooks/pre-applypatch
+ hook="$(git rev-parse --git-path hooks/pre-applypatch)"
+ if test -x "$hook"
then
- "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+ "$hook" || stop_here $this
fi
tree=$(git write-tree) &&
@@ -908,18 +909,17 @@ did you forget to use 'git add'?"
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
fi
- if test -x "$GIT_DIR"/hooks/post-applypatch
- then
- "$GIT_DIR"/hooks/post-applypatch
- fi
+ hook="$(git rev-parse --git-path hooks/post-applypatch)"
+ test -x "$hook" && "$hook"
go_next
done
if test -s "$dotest"/rewritten; then
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
- if test -x "$GIT_DIR"/hooks/post-rewrite; then
- "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
+ if test -x "$hook"; then
+ "$hook" rebase < "$dotest"/rewritten
fi
fi
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 43c19e0..dd84f9d 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -632,9 +632,9 @@ do_next () {
git notes copy --for-rewrite=rebase < "$rewritten_list" ||
true # we don't care if this copying failed
} &&
- if test -x "$GIT_DIR"/hooks/post-rewrite &&
- test -s "$rewritten_list"; then
- "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
+ if test -x "$hook" && test -s "$rewritten_list"; then
+ "$hook" rebase < "$rewritten_list"
true # we don't care if this hook failed
fi &&
warn "Successfully rebased and updated $head_name."
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index e7d96de..93d7bcf 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -93,10 +93,8 @@ finish_rb_merge () {
if test -s "$state_dir"/rewritten
then
git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
- if test -x "$GIT_DIR"/hooks/post-rewrite
- then
- "$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
- fi
+ hook="$(git rev-parse --git-path hooks/post-rewrite)"
+ test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
fi
say All done.
}
diff --git a/git-rebase.sh b/git-rebase.sh
index 8a3efa2..b90a6d9 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -195,9 +195,9 @@ run_specific_rebase () {
run_pre_rebase_hook () {
if test -z "$ok_to_skip_pre_rebase" &&
- test -x "$GIT_DIR/hooks/pre-rebase"
+ test -x "$(git rev-parse --git-path hooks/pre-rebase)"
then
- "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+ "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
die "$(gettext "The pre-rebase hook refused to rebase.")"
fi
}
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
index 8b2a2fe..a5d7b84 100755
--- a/templates/hooks--applypatch-msg.sample
+++ b/templates/hooks--applypatch-msg.sample
@@ -10,6 +10,6 @@
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
-test -x "$GIT_DIR/hooks/commit-msg" &&
- exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
index b1f187c..4142082 100755
--- a/templates/hooks--pre-applypatch.sample
+++ b/templates/hooks--pre-applypatch.sample
@@ -9,6 +9,6 @@
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
-test -x "$GIT_DIR/hooks/pre-commit" &&
- exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 14/28] git-stash: avoid hardcoding $GIT_DIR/logs/....
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (12 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 13/28] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 15/28] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
` (15 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
git-stash.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-stash.sh b/git-stash.sh
index 0de9a6c..8093326 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -183,7 +183,7 @@ store_stash () {
fi
# Make sure the reflog for stash is kept.
- : >>"$GIT_DIR/logs/$ref_stash"
+ : >>"$(git rev-parse --git-path logs/$ref_stash)"
git update-ref -m "$stash_msg" $ref_stash $w_commit
ret=$?
test $ret != 0 && test -z $quiet &&
@@ -258,7 +258,7 @@ save_stash () {
say "$(gettext "No local changes to save")"
exit 0
fi
- test -f "$GIT_DIR/logs/$ref_stash" ||
+ test -f "$(git rev-parse --git-path logs/$ref_stash)" ||
clear_stash || die "$(gettext "Cannot initialize stash")"
create_stash "$stash_msg" $untracked
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 15/28] setup.c: convert is_git_directory() to use strbuf
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (13 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 14/28] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 16/28] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
` (14 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/setup.c b/setup.c
index 6c3f85f..4994437 100644
--- a/setup.c
+++ b/setup.c
@@ -184,31 +184,36 @@ void verify_non_filename(const char *prefix, const char *arg)
*/
int is_git_directory(const char *suspect)
{
- char path[PATH_MAX];
- size_t len = strlen(suspect);
+ struct strbuf path = STRBUF_INIT;
+ int ret = 0;
+ size_t len;
- if (PATH_MAX <= len + strlen("/objects"))
- die("Too long path: %.*s", 60, suspect);
- strcpy(path, suspect);
+ strbuf_addstr(&path, suspect);
+ len = path.len;
if (getenv(DB_ENVIRONMENT)) {
if (access(getenv(DB_ENVIRONMENT), X_OK))
- return 0;
+ goto done;
}
else {
- strcpy(path + len, "/objects");
- if (access(path, X_OK))
- return 0;
+ strbuf_addstr(&path, "/objects");
+ if (access(path.buf, X_OK))
+ goto done;
}
- strcpy(path + len, "/refs");
- if (access(path, X_OK))
- return 0;
+ strbuf_setlen(&path, len);
+ strbuf_addstr(&path, "/refs");
+ if (access(path.buf, X_OK))
+ goto done;
- strcpy(path + len, "/HEAD");
- if (validate_headref(path))
- return 0;
+ strbuf_setlen(&path, len);
+ strbuf_addstr(&path, "/HEAD");
+ if (validate_headref(path.buf))
+ goto done;
- return 1;
+ ret = 1;
+done:
+ strbuf_release(&path);
+ return ret;
}
int is_inside_git_dir(void)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 16/28] setup.c: detect $GIT_COMMON_DIR in is_git_directory()
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (14 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 15/28] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 17/28] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
` (13 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
If the file "$GIT_DIR/commondir" exists, it contains the value of
$GIT_COMMON_DIR.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/gitrepository-layout.txt | 7 ++++++
setup.c | 43 +++++++++++++++++++++++++++++-----
2 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 556da09..c6f3649 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -233,6 +233,13 @@ shallow::
file is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/shallow" will be used instead.
+commondir::
+ If this file exists, $GIT_COMMON_DIR (see linkgit:git[1]) will
+ be set to the path specified in this file if it is not
+ explicitly set. If the specified path is relative, it is
+ relative to $GIT_DIR. The repository with commondir is
+ incomplete without the repository pointed by "commondir".
+
modules::
Contains the git-repositories of the submodules. This
directory is ignored if $GIT_COMMON_DIR is set and
diff --git a/setup.c b/setup.c
index 4994437..7e5b334 100644
--- a/setup.c
+++ b/setup.c
@@ -170,6 +170,33 @@ void verify_non_filename(const char *prefix, const char *arg)
"'git <command> [<revision>...] -- [<file>...]'", arg);
}
+static void get_common_dir(struct strbuf *sb, const char *gitdir)
+{
+ struct strbuf data = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (git_common_dir) {
+ strbuf_addstr(sb, git_common_dir);
+ return;
+ }
+ strbuf_addf(&path, "%s/commondir", gitdir);
+ if (file_exists(path.buf)) {
+ if (strbuf_read_file(&data, path.buf, 0) <= 0)
+ die_errno(_("failed to read %s"), path.buf);
+ while (data.len && (data.buf[data.len - 1] == '\n' ||
+ data.buf[data.len - 1] == '\r'))
+ data.len--;
+ data.buf[data.len] = '\0';
+ strbuf_reset(&path);
+ if (!is_absolute_path(data.buf))
+ strbuf_addf(&path, "%s/", gitdir);
+ strbuf_addbuf(&path, &data);
+ strbuf_addstr(sb, real_path(path.buf));
+ } else
+ strbuf_addstr(sb, gitdir);
+ strbuf_release(&data);
+ strbuf_release(&path);
+}
/*
* Test if it looks like we're at a git directory.
@@ -188,13 +215,22 @@ int is_git_directory(const char *suspect)
int ret = 0;
size_t len;
- strbuf_addstr(&path, suspect);
+ /* Check worktree-related signatures */
+ strbuf_addf(&path, "%s/HEAD", suspect);
+ if (validate_headref(path.buf))
+ goto done;
+
+ strbuf_reset(&path);
+ get_common_dir(&path, suspect);
len = path.len;
+
+ /* Check non-worktree-related signatures */
if (getenv(DB_ENVIRONMENT)) {
if (access(getenv(DB_ENVIRONMENT), X_OK))
goto done;
}
else {
+ strbuf_setlen(&path, len);
strbuf_addstr(&path, "/objects");
if (access(path.buf, X_OK))
goto done;
@@ -205,11 +241,6 @@ int is_git_directory(const char *suspect)
if (access(path.buf, X_OK))
goto done;
- strbuf_setlen(&path, len);
- strbuf_addstr(&path, "/HEAD");
- if (validate_headref(path.buf))
- goto done;
-
ret = 1;
done:
strbuf_release(&path);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 17/28] setup.c: convert check_repository_format_gently to use strbuf
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (15 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 16/28] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 18/28] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
` (12 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/setup.c b/setup.c
index 7e5b334..5085ab1 100644
--- a/setup.c
+++ b/setup.c
@@ -288,7 +288,9 @@ void setup_work_tree(void)
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
{
- char repo_config[PATH_MAX+1];
+ struct strbuf sb = STRBUF_INIT;
+ const char *repo_config;
+ int ret = 0;
/*
* git_config() can't be used here because it calls git_pathdup()
@@ -299,7 +301,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
+ strbuf_addf(&sb, "%s/config", gitdir);
+ repo_config = sb.buf;
git_config_early(check_repository_format_version, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
@@ -309,9 +312,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
GIT_REPO_VERSION, repository_format_version);
warning("Please upgrade Git");
*nongit_ok = -1;
- return -1;
+ ret = -1;
}
- return 0;
+ strbuf_release(&sb);
+ return ret;
}
/*
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 18/28] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently()
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (16 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 17/28] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 19/28] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
` (11 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
setup.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/setup.c b/setup.c
index 5085ab1..42849f3 100644
--- a/setup.c
+++ b/setup.c
@@ -292,6 +292,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
const char *repo_config;
int ret = 0;
+ get_common_dir(&sb, gitdir);
+ strbuf_addstr(&sb, "/config");
+ repo_config = sb.buf;
+
/*
* git_config() can't be used here because it calls git_pathdup()
* to get $GIT_CONFIG/config. That call will make setup_git_env()
@@ -301,8 +305,6 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- strbuf_addf(&sb, "%s/config", gitdir);
- repo_config = sb.buf;
git_config_early(check_repository_format_version, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 19/28] setup.c: support multi-checkout repo setup
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (17 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 18/28] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 20/28] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
` (10 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The repo setup procedure is updated to detect $GIT_DIR/commondir and
set $GIT_COMMON_DIR properly.
The core.worktree is ignored when $GIT_COMMON_DIR is set. This is
because the config file is shared in multi-checkout setup, but
checkout directories _are_ different. Making core.worktree effective
in all checkouts mean it's back to a single checkout.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 2 ++
Documentation/git-rev-parse.txt | 3 ++
builtin/rev-parse.c | 4 +++
cache.h | 1 +
environment.c | 8 ++---
setup.c | 33 +++++++++++++-----
t/t1501-worktree.sh | 76 +++++++++++++++++++++++++++++++++++++++++
t/t1510-repo-setup.sh | 1 +
trace.c | 1 +
9 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5f4d793..313d4b3 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -381,6 +381,8 @@ false), while all other repositories are assumed to be bare (bare
core.worktree::
Set the path to the root of the working tree.
+ If GIT_COMMON_DIR environment variable is set, core.worktree
+ is ignored and not used for determining the root of working tree.
This can be overridden by the GIT_WORK_TREE environment
variable and the '--work-tree' command line option.
The value can be an absolute path or relative to the path to
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 46020d9..8a9cac8 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -215,6 +215,9 @@ If `$GIT_DIR` is not defined and the current directory
is not detected to lie in a Git repository or work tree
print a message to stderr and exit with nonzero status.
+--git-common-dir::
+ Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
+
--is-inside-git-dir::
When the current working directory is below the repository
directory print "true", otherwise "false".
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index e50bc65..c7057ce 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -744,6 +744,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
continue;
}
+ if (!strcmp(arg, "--git-common-dir")) {
+ puts(get_git_common_dir());
+ continue;
+ }
if (!strcmp(arg, "--resolve-git-dir")) {
const char *gitdir = resolve_gitdir(argv[i+1]);
if (!gitdir)
diff --git a/cache.h b/cache.h
index 9182427..992e241 100644
--- a/cache.h
+++ b/cache.h
@@ -407,6 +407,7 @@ extern char *get_object_directory(void);
extern char *get_index_file(void);
extern char *get_graft_file(void);
extern int set_git_dir(const char *path);
+extern int get_common_dir(struct strbuf *sb, const char *gitdir);
extern const char *get_git_namespace(void);
extern const char *strip_namespace(const char *namespaced_ref);
extern const char *get_git_work_tree(void);
diff --git a/environment.c b/environment.c
index c998120..0999fc1 100644
--- a/environment.c
+++ b/environment.c
@@ -126,6 +126,7 @@ static char *expand_namespace(const char *raw_namespace)
static void setup_git_env(void)
{
+ struct strbuf sb = STRBUF_INIT;
const char *gitfile;
const char *shallow_file;
@@ -134,12 +135,9 @@ static void setup_git_env(void)
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
gitfile = read_gitfile(git_dir);
git_dir = xstrdup(gitfile ? gitfile : git_dir);
- git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
- if (git_common_dir) {
+ if (get_common_dir(&sb, git_dir))
git_common_dir_env = 1;
- git_common_dir = xstrdup(git_common_dir);
- } else
- git_common_dir = git_dir;
+ git_common_dir = strbuf_detach(&sb, NULL);
git_object_dir = getenv(DB_ENVIRONMENT);
if (!git_object_dir) {
git_object_dir = xmalloc(strlen(git_common_dir) + 9);
diff --git a/setup.c b/setup.c
index 42849f3..40ce191 100644
--- a/setup.c
+++ b/setup.c
@@ -170,14 +170,15 @@ void verify_non_filename(const char *prefix, const char *arg)
"'git <command> [<revision>...] -- [<file>...]'", arg);
}
-static void get_common_dir(struct strbuf *sb, const char *gitdir)
+int get_common_dir(struct strbuf *sb, const char *gitdir)
{
struct strbuf data = STRBUF_INIT;
struct strbuf path = STRBUF_INIT;
const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ int ret = 0;
if (git_common_dir) {
strbuf_addstr(sb, git_common_dir);
- return;
+ return 1;
}
strbuf_addf(&path, "%s/commondir", gitdir);
if (file_exists(path.buf)) {
@@ -192,10 +193,12 @@ static void get_common_dir(struct strbuf *sb, const char *gitdir)
strbuf_addf(&path, "%s/", gitdir);
strbuf_addbuf(&path, &data);
strbuf_addstr(sb, real_path(path.buf));
+ ret = 1;
} else
strbuf_addstr(sb, gitdir);
strbuf_release(&data);
strbuf_release(&path);
+ return ret;
}
/*
@@ -286,13 +289,26 @@ void setup_work_tree(void)
initialized = 1;
}
+static int check_repo_format(const char *var, const char *value, void *cb)
+{
+ if (strcmp(var, "core.repositoryformatversion") == 0)
+ repository_format_version = git_config_int(var, value);
+ else if (strcmp(var, "core.sharedrepository") == 0)
+ shared_repository = git_config_perm(var, value);
+ return 0;
+}
+
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
{
struct strbuf sb = STRBUF_INIT;
const char *repo_config;
+ config_fn_t fn;
int ret = 0;
- get_common_dir(&sb, gitdir);
+ if (get_common_dir(&sb, gitdir))
+ fn = check_repo_format;
+ else
+ fn = check_repository_format_version;
strbuf_addstr(&sb, "/config");
repo_config = sb.buf;
@@ -305,7 +321,7 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
* Use a gentler version of git_config() to check if this repo
* is a good one.
*/
- git_config_early(check_repository_format_version, NULL, repo_config);
+ git_config_early(fn, NULL, repo_config);
if (GIT_REPO_VERSION < repository_format_version) {
if (!nongit_ok)
die ("Expected git repo version <= %d, found %d",
@@ -777,11 +793,10 @@ int git_config_perm(const char *var, const char *value)
int check_repository_format_version(const char *var, const char *value, void *cb)
{
- if (strcmp(var, "core.repositoryformatversion") == 0)
- repository_format_version = git_config_int(var, value);
- else if (strcmp(var, "core.sharedrepository") == 0)
- shared_repository = git_config_perm(var, value);
- else if (strcmp(var, "core.bare") == 0) {
+ int ret = check_repo_format(var, value, cb);
+ if (ret)
+ return ret;
+ if (strcmp(var, "core.bare") == 0) {
is_bare_repository_cfg = git_config_bool(var, value);
if (is_bare_repository_cfg == 1)
inside_work_tree = -1;
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index 8f36aa9..2ac4424 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -346,4 +346,80 @@ test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
test_cmp expected actual
'
+test_expect_success 'Multi-worktree setup' '
+ mkdir work &&
+ mkdir -p repo.git/repos/foo &&
+ cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
+ sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'GIT_DIR set (1)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'GIT_DIR set (2)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo "$TRASH_DIRECTORY/repo.git" >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Auto discovery' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data1 &&
+ git add data1 &&
+ git ls-files --full-name :/ | grep data1 >actual &&
+ echo work/data1 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_DIR/common overrides core.worktree' '
+ mkdir elsewhere &&
+ git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ echo "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data2 &&
+ git add data2 &&
+ git ls-files --full-name :/ | grep data2 >actual &&
+ echo work/data2 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ echo haha >data3 &&
+ git --git-dir=../.git --work-tree=. add data3 &&
+ git ls-files --full-name -- :/ | grep data3 >actual &&
+ echo data3 >expect &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index cf2ee78..c211ef5 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -106,6 +106,7 @@ setup_env () {
expect () {
cat >"$1/expected" <<-EOF
setup: git_dir: $2
+ setup: git_common_dir: $2
setup: worktree: $3
setup: cwd: $4
setup: prefix: $5
diff --git a/trace.c b/trace.c
index 08180a9..a594761 100644
--- a/trace.c
+++ b/trace.c
@@ -173,6 +173,7 @@ void trace_repo_setup(const char *prefix)
prefix = "(null)";
trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+ trace_printf_key(key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_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));
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 20/28] wrapper.c: wrapper to open a file, fprintf then close
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (18 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 19/28] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 21/28] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
` (9 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
cache.h | 2 ++
wrapper.c | 31 +++++++++++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/cache.h b/cache.h
index 992e241..979ac6e 100644
--- a/cache.h
+++ b/cache.h
@@ -1239,6 +1239,8 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
{
return write_in_full(fd, str, strlen(str));
}
+__attribute__((format (printf,3,4)))
+extern int write_file(const char *path, int fatal, const char *fmt, ...);
/* pager.c */
extern void setup_pager(void);
diff --git a/wrapper.c b/wrapper.c
index 0cc5636..5ad628c 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -455,3 +455,34 @@ struct passwd *xgetpwuid_self(void)
errno ? strerror(errno) : _("no such user"));
return pw;
}
+
+int write_file(const char *path, int fatal, const char *fmt, ...)
+{
+ struct strbuf sb = STRBUF_INIT;
+ va_list params;
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0) {
+ if (fatal)
+ die_errno(_("could not open %s for writing"), path);
+ return -1;
+ }
+ va_start(params, fmt);
+ strbuf_vaddf(&sb, fmt, params);
+ va_end(params);
+ if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
+ int err = errno;
+ close(fd);
+ strbuf_release(&sb);
+ errno = err;
+ if (fatal)
+ die_errno(_("could not write to %s"), path);
+ return -1;
+ }
+ strbuf_release(&sb);
+ if (close(fd)) {
+ if (fatal)
+ die_errno(_("could not close %s"), path);
+ return -1;
+ }
+ return 0;
+}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 21/28] use new wrapper write_file() for simple file writing
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (19 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 20/28] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 22/28] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
` (8 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
This fixes common problems in these code about error handling,
forgetting to close the file handle after fprintf() fails, or not
printing out the error string..
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/branch.c | 4 +---
builtin/init-db.c | 7 +------
daemon.c | 11 +----------
submodule.c | 9 ++-------
transport.c | 8 +++-----
5 files changed, 8 insertions(+), 31 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index b4d7716..3eebdbc 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -754,7 +754,6 @@ static const char edit_description[] = "BRANCH_DESCRIPTION";
static int edit_branch_description(const char *branch_name)
{
- FILE *fp;
int status;
struct strbuf buf = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
@@ -767,8 +766,7 @@ static int edit_branch_description(const char *branch_name)
" %s\n"
"Lines starting with '%c' will be stripped.\n",
branch_name, comment_line_char);
- fp = fopen(git_path(edit_description), "w");
- if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+ if (write_file(git_path(edit_description), 0, "%s", buf.buf)) {
strbuf_release(&buf);
return error(_("could not write branch description template: %s"),
strerror(errno));
diff --git a/builtin/init-db.c b/builtin/init-db.c
index c7c76bb..081e512 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -342,7 +342,6 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
static void separate_git_dir(const char *git_dir)
{
struct stat st;
- FILE *fp;
if (!stat(git_link, &st)) {
const char *src;
@@ -358,11 +357,7 @@ static void separate_git_dir(const char *git_dir)
die_errno(_("unable to move %s to %s"), src, git_dir);
}
- fp = fopen(git_link, "w");
- if (!fp)
- die(_("Could not create git link %s"), git_link);
- fprintf(fp, "gitdir: %s\n", git_dir);
- fclose(fp);
+ write_file(git_link, 1, "gitdir: %s\n", git_dir);
}
int init_db(const char *template_dir, unsigned int flags)
diff --git a/daemon.c b/daemon.c
index 503e039..b880d30 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1122,15 +1122,6 @@ static void daemonize(void)
}
#endif
-static void store_pid(const char *path)
-{
- FILE *f = fopen(path, "w");
- if (!f)
- die_errno("cannot open pid file '%s'", path);
- if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
- die_errno("failed to write pid file '%s'", path);
-}
-
static int serve(struct string_list *listen_addr, int listen_port,
struct credentials *cred)
{
@@ -1339,7 +1330,7 @@ int main(int argc, char **argv)
sanitize_stdfds();
if (pid_file)
- store_pid(pid_file);
+ write_file(pid_file, 1, "%"PRIuMAX"\n", (uintmax_t) getpid());
/* prepare argv for serving-processes */
cld_argv = xmalloc(sizeof (char *) * (argc + 2));
diff --git a/submodule.c b/submodule.c
index 613857e..fe5748d 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1135,16 +1135,11 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
struct strbuf file_name = STRBUF_INIT;
struct strbuf rel_path = STRBUF_INIT;
const char *real_work_tree = xstrdup(real_path(work_tree));
- FILE *fp;
/* Update gitfile */
strbuf_addf(&file_name, "%s/.git", work_tree);
- fp = fopen(file_name.buf, "w");
- if (!fp)
- die(_("Could not create git link %s"), file_name.buf);
- fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
- &rel_path));
- fclose(fp);
+ write_file(file_name.buf, 1, "gitdir: %s\n",
+ relative_path(git_dir, real_work_tree, &rel_path));
/* Update core.worktree setting */
strbuf_reset(&file_name);
diff --git a/transport.c b/transport.c
index ca7bb44..2df8a15 100644
--- a/transport.c
+++ b/transport.c
@@ -294,7 +294,6 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
{
struct strbuf *buf = data;
int len = buf->len;
- FILE *f;
/* when called via for_each_ref(), flags is non-zero */
if (flags && !starts_with(name, "refs/heads/") &&
@@ -303,10 +302,9 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
strbuf_addstr(buf, name);
if (safe_create_leading_directories(buf->buf) ||
- !(f = fopen(buf->buf, "w")) ||
- fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
- fclose(f))
- return error("problems writing temporary file %s", buf->buf);
+ write_file(buf->buf, 0, "%s\n", sha1_to_hex(sha1)))
+ return error("problems writing temporary file %s: %s",
+ buf->buf, strerror(errno));
strbuf_setlen(buf, len);
return 0;
}
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 22/28] checkout: support checking out into a new working directory
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (20 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 21/28] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 23/28] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
` (7 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
"git checkout --to" sets up a new working directory with a .git file
pointing to $GIT_DIR/repos/<id>. It then executes "git checkout" again
on the new worktree with the same arguments except "--to" is taken
out. The second checkout execution, which is not contaminated with any
info from the current repository, will actually check out and
everything that normal "git checkout" does.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-checkout.txt | 34 +++++++++++++
Documentation/git.txt | 3 +-
Documentation/gitrepository-layout.txt | 7 +++
builtin/checkout.c | 93 +++++++++++++++++++++++++++++++++-
path.c | 2 +-
t/t2025-checkout-to.sh (new +x) | 48 ++++++++++++++++++
6 files changed, 183 insertions(+), 4 deletions(-)
create mode 100755 t/t2025-checkout-to.sh
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 33ad2ad..fcf73b2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -225,6 +225,13 @@ This means that you can use `git checkout -p` to selectively discard
edits from your current working tree. See the ``Interactive Mode''
section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
+--to=<path>::
+ Check out a new branch in a separate working directory at
+ `<path>`. A new working directory is linked to the current
+ repository, sharing everything except working directory
+ specific files such as HEAD, index... See "MULTIPLE CHECKOUT
+ MODE" section for more information.
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
@@ -388,6 +395,33 @@ $ git reflog -2 HEAD # or
$ git log -g -2 HEAD
------------
+MULTIPLE CHECKOUT MODE
+-------------------------------
+Normally a working directory is attached to repository. When "git
+checkout --to" is used, a new working directory is attached to the
+current repository. This new working directory is called "linked
+checkout" as compared to the "main checkout" prepared by "git init" or
+"git clone". A repository has one main checkout and zero or more
+linked checkouts.
+
+All checkouts share the same repository. Linked checkouts see the
+repository a bit different from the main checkout. When the checkout
+"new" reads the path $GIT_DIR/HEAD for example, the actual path
+returned could be $GIT_DIR/repos/new/HEAD. This ensures checkouts
+won't step on each other.
+
+Each linked checkout has a private space in $GIT_DIR/repos, usually
+named after the base name of the working directory with a number added
+to make it unique. The linked checkout's $GIT_DIR points to this
+private space while $GIT_COMMON_DIR points to the main checkout's
+$GIT_DIR. These settings are done by "git checkout --to".
+
+Because in this mode $GIT_DIR becomes a lightweight virtual file
+system where a path could be rewritten to some place else, accessing
+$GIT_DIR from scripts should use `git rev-parse --git-path` to resolve
+a path instead of using it directly unless the path is known to be
+private to the working directory.
+
EXAMPLES
--------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b094b1f..bdb9b0f 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -777,7 +777,8 @@ Git so take care if using Cogito etc.
If this variable is set to a path, non-worktree files that are
normally in $GIT_DIR will be taken from this path
instead. Worktree-specific files such as HEAD or index are
- taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for
+ taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and
+ the section 'MULTIPLE CHECKOUT MODE' in linkgit:checkout[1]
details. This variable has lower precedence than other path
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index c6f3649..2206f35 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -245,6 +245,13 @@ modules::
directory is ignored if $GIT_COMMON_DIR is set and
"$GIT_COMMON_DIR/modules" will be used instead.
+repos::
+ Contains worktree specific information of linked
+ checkouts. Each subdirectory contains the worktree-related
+ part of a linked checkout. This directory is ignored
+ $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/repos" will be
+ used instead.
+
SEE ALSO
--------
linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efb5e2f..aa4ffd1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -48,6 +48,10 @@ struct checkout_opts {
const char *prefix;
struct pathspec pathspec;
struct tree *source_tree;
+
+ const char *new_worktree;
+ const char **saved_argv;
+ int new_worktree_mode;
};
static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -250,6 +254,9 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("Cannot update paths and switch to branch '%s' at the same time."),
opts->new_branch);
+ if (opts->new_worktree)
+ die(_("'%s' cannot be used with updating paths"), "--to");
+
if (opts->patch_mode)
return run_add_interactive(revision, "--patch=checkout",
&opts->pathspec);
@@ -486,7 +493,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.dir->flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(topts.dir);
}
- tree = parse_tree_indirect(old->commit ?
+ tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?
old->commit->object.sha1 :
EMPTY_TREE_SHA1_BIN);
init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -797,7 +804,8 @@ static int switch_branches(const struct checkout_opts *opts,
return ret;
}
- if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
+ if (!opts->quiet && !old.path && old.commit &&
+ new->commit != old.commit && !opts->new_worktree_mode)
orphaned_commit_warning(old.commit, new->commit);
update_refs_for_switch(opts, &old, new);
@@ -807,6 +815,74 @@ static int switch_branches(const struct checkout_opts *opts,
return ret || writeout_error;
}
+static int prepare_linked_checkout(const struct checkout_opts *opts,
+ struct branch_info *new)
+{
+ struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ const char *path = opts->new_worktree, *name;
+ struct stat st;
+ struct child_process cp;
+ int counter = 0, len;
+
+ if (!new->commit)
+ die(_("no branch specified"));
+
+ len = strlen(path);
+ while (len && is_dir_sep(path[len - 1]))
+ len--;
+
+ for (name = path + len - 1; name > path; name--)
+ if (is_dir_sep(*name)) {
+ name++;
+ break;
+ }
+ strbuf_addstr(&sb_repo,
+ git_path("repos/%.*s", (int)(path + len - name), name));
+ len = sb_repo.len;
+ if (safe_create_leading_directories_const(sb_repo.buf))
+ die_errno(_("could not create leading directories of '%s'"),
+ sb_repo.buf);
+ while (!stat(sb_repo.buf, &st)) {
+ counter++;
+ strbuf_setlen(&sb_repo, len);
+ strbuf_addf(&sb_repo, "%d", counter);
+ }
+ name = strrchr(sb_repo.buf, '/') + 1;
+ if (mkdir(sb_repo.buf, 0777))
+ die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+
+ strbuf_addf(&sb_git, "%s/.git", path);
+ if (safe_create_leading_directories_const(sb_git.buf))
+ die_errno(_("could not create leading directories of '%s'"),
+ sb_git.buf);
+
+ write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
+ real_path(get_git_common_dir()), name);
+ /*
+ * This is to keep resolve_ref() happy. We need a valid HEAD
+ * or is_git_directory() will reject the directory. Any valid
+ * value would do because this value will be ignored and
+ * replaced at the next (real) checkout.
+ */
+ strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+ write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
+ write_file(sb.buf, 1, "../..\n");
+
+ if (!opts->quiet)
+ fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
+
+ setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
+ setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
+ setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
+ memset(&cp, 0, sizeof(cp));
+ cp.git_cmd = 1;
+ cp.argv = opts->saved_argv;
+ return run_command(&cp);
+}
+
static int git_checkout_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.ignoresubmodules")) {
@@ -1068,6 +1144,9 @@ static int checkout_branch(struct checkout_opts *opts,
die(_("Cannot switch branch to a non-commit '%s'"),
new->name);
+ if (opts->new_worktree)
+ return prepare_linked_checkout(opts, new);
+
if (!new->commit && opts->new_branch) {
unsigned char rev[20];
int flag;
@@ -1110,6 +1189,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("do not limit pathspecs to sparse entries only")),
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
N_("second guess 'git checkout no-such-branch'")),
+ OPT_STRING(0, "to", &opts.new_worktree, N_("path"),
+ N_("check a branch out in a separate working directory")),
OPT_END(),
};
@@ -1118,6 +1199,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.overwrite_ignore = 1;
opts.prefix = prefix;
+ opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
+ memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
+
gitmodules_config();
git_config(git_checkout_config, &opts);
@@ -1126,6 +1210,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ /* recursive execution from checkout_new_worktree() */
+ opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
+ if (opts.new_worktree_mode)
+ opts.new_worktree = NULL;
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --git a/path.c b/path.c
index 8453687..ddb5962 100644
--- a/path.c
+++ b/path.c
@@ -92,7 +92,7 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
static const char *common_list[] = {
"/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules",
- "/objects", "/refs", "/remotes", "/rr-cache", "/svn",
+ "/objects", "/refs", "/remotes", "/repos", "/rr-cache", "/svn",
"config", "gc.pid", "packed-refs", "shallow",
NULL
};
diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh
new file mode 100755
index 0000000..5ec49e2
--- /dev/null
+++ b/t/t2025-checkout-to.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test git checkout --to'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+test_expect_success 'checkout --to not updating paths' '
+ test_must_fail git checkout --to -- init.t
+'
+
+test_expect_success 'checkout --to a new worktree' '
+ git checkout --to here master &&
+ (
+ cd here &&
+ test_cmp ../init.t init.t &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/master >expect &&
+ test_cmp expect actual &&
+ git fsck
+ )
+'
+
+test_expect_success 'checkout --to from a linked checkout' '
+ (
+ cd here &&
+ git checkout --to nested-here master
+ cd nested-here &&
+ git fsck
+ )
+'
+
+test_expect_success 'checkout --to a new worktree creating new branch' '
+ git checkout --to there -b newmaster master &&
+ (
+ cd there &&
+ test_cmp ../init.t init.t &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/newmaster >expect &&
+ test_cmp expect actual &&
+ git fsck
+ )
+'
+
+test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 23/28] checkout: clean up half-prepared directories in --to mode
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (21 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 22/28] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 24/28] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
` (6 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index aa4ffd1..97fdfcc 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -20,6 +20,7 @@
#include "resolve-undo.h"
#include "submodule.h"
#include "argv-array.h"
+#include "sigchain.h"
static const char * const checkout_usage[] = {
N_("git checkout [options] <branch>"),
@@ -815,6 +816,35 @@ static int switch_branches(const struct checkout_opts *opts,
return ret || writeout_error;
}
+static const char *junk_work_tree;
+static const char *junk_git_dir;
+static int is_junk;
+static pid_t junk_pid;
+
+static void remove_junk(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (!is_junk || getpid() != junk_pid)
+ return;
+ if (junk_git_dir) {
+ strbuf_addstr(&sb, junk_git_dir);
+ remove_dir_recursively(&sb, 0);
+ strbuf_reset(&sb);
+ }
+ if (junk_work_tree) {
+ strbuf_addstr(&sb, junk_work_tree);
+ remove_dir_recursively(&sb, 0);
+ }
+ strbuf_release(&sb);
+}
+
+static void remove_junk_on_signal(int signo)
+{
+ remove_junk();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
static int prepare_linked_checkout(const struct checkout_opts *opts,
struct branch_info *new)
{
@@ -823,7 +853,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
const char *path = opts->new_worktree, *name;
struct stat st;
struct child_process cp;
- int counter = 0, len;
+ int counter = 0, len, ret;
if (!new->commit)
die(_("no branch specified"));
@@ -849,13 +879,21 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
strbuf_addf(&sb_repo, "%d", counter);
}
name = strrchr(sb_repo.buf, '/') + 1;
+
+ junk_pid = getpid();
+ atexit(remove_junk);
+ sigchain_push_common(remove_junk_on_signal);
+
if (mkdir(sb_repo.buf, 0777))
die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+ junk_git_dir = sb_repo.buf;
+ is_junk = 1;
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
+ junk_work_tree = path;
write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
real_path(get_git_common_dir()), name);
@@ -880,7 +918,14 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
memset(&cp, 0, sizeof(cp));
cp.git_cmd = 1;
cp.argv = opts->saved_argv;
- return run_command(&cp);
+ ret = run_command(&cp);
+ if (!ret)
+ is_junk = 0;
+ strbuf_release(&sb);
+ strbuf_release(&sb_repo);
+ strbuf_release(&sb_git);
+ return ret;
+
}
static int git_checkout_config(const char *var, const char *value, void *cb)
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 24/28] checkout: detach if the branch is already checked out elsewhere
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (22 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 23/28] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 25/28] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
` (5 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
The normal rule is anything outside refs/heads/ is detached. This
increases strictness of the rule a bit more: if the branch is checked
out (either in $GIT_COMMON_DIR/HEAD or any $GIT_DIR/repos/.../HEAD)
then it's detached as well.
A hint is given so the user knows where to go and do something there
if they still want to checkout undetached here.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/checkout.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++
t/t2025-checkout-to.sh | 15 ++++++++--
2 files changed, 92 insertions(+), 3 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 97fdfcc..598b43d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -433,6 +433,11 @@ struct branch_info {
const char *name; /* The short name used */
const char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
+ /*
+ * if not null the branch is detached because it's already
+ * checked out in this checkout
+ */
+ char *checkout;
};
static void setup_branch_path(struct branch_info *branch)
@@ -641,6 +646,11 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (old->path && advice_detached_head)
detach_advice(new->name);
describe_detached_head(_("HEAD is now at"), new->commit);
+ if (new->checkout && !*new->checkout)
+ fprintf(stderr, _("hint: the main checkout is holding this branch\n"));
+ else if (new->checkout)
+ fprintf(stderr, _("hint: the linked checkout %s is holding this branch\n"),
+ new->checkout);
}
} else if (new->path) { /* Switch branches. */
create_symref("HEAD", new->path, msg.buf);
@@ -983,6 +993,73 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
return NULL;
}
+static int check_linked_checkout(struct branch_info *new,
+ const char *name, const char *path)
+{
+ struct strbuf sb = STRBUF_INIT;
+ char *start, *end;
+ if (strbuf_read_file(&sb, path, 0) < 0)
+ return 0;
+ if (!starts_with(sb.buf, "ref:")) {
+ strbuf_release(&sb);
+ return 0;
+ }
+
+ start = sb.buf + 4;
+ while (isspace(*start))
+ start++;
+ end = start;
+ while (*end && !isspace(*end))
+ end++;
+ if (!strncmp(start, new->path, end - start) &&
+ new->path[end - start] == '\0') {
+ strbuf_release(&sb);
+ new->path = NULL; /* detach */
+ new->checkout = xstrdup(name); /* reason */
+ return 1;
+ }
+ strbuf_release(&sb);
+ return 0;
+}
+
+static void check_linked_checkouts(struct branch_info *new)
+{
+ struct strbuf path = STRBUF_INIT;
+ DIR *dir;
+ struct dirent *d;
+
+ strbuf_addf(&path, "%s/repos", get_git_common_dir());
+ if ((dir = opendir(path.buf)) == NULL) {
+ strbuf_release(&path);
+ return;
+ }
+
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+ /*
+ * $GIT_COMMON_DIR/HEAD is practically outside
+ * $GIT_DIR so resolve_ref_unsafe() won't work (it
+ * uses git_path). Parse the ref ourselves.
+ */
+ if (check_linked_checkout(new, "", path.buf)) {
+ strbuf_release(&path);
+ closedir(dir);
+ return;
+ }
+
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/repos/%s/HEAD",
+ get_git_common_dir(), d->d_name);
+ if (check_linked_checkout(new, d->d_name, path.buf))
+ break;
+ }
+ strbuf_release(&path);
+ closedir(dir);
+}
+
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new,
@@ -1110,6 +1187,9 @@ static int parse_branchname_arg(int argc, const char **argv,
else
new->path = NULL; /* not an existing branch */
+ if (new->path)
+ check_linked_checkouts(new);
+
new->commit = lookup_commit_reference_gently(rev, 1);
if (!new->commit) {
/* not a commit */
diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh
index 5ec49e2..2d35a9b 100755
--- a/t/t2025-checkout-to.sh
+++ b/t/t2025-checkout-to.sh
@@ -13,13 +13,14 @@ test_expect_success 'checkout --to not updating paths' '
'
test_expect_success 'checkout --to a new worktree' '
+ git rev-parse HEAD >expect &&
git checkout --to here master &&
(
cd here &&
test_cmp ../init.t init.t &&
- git symbolic-ref HEAD >actual &&
- echo refs/heads/master >expect &&
- test_cmp expect actual &&
+ test_must_fail git symbolic-ref HEAD &&
+ git rev-parse HEAD >actual &&
+ test_cmp ../expect actual &&
git fsck
)
'
@@ -45,4 +46,12 @@ test_expect_success 'checkout --to a new worktree creating new branch' '
)
'
+test_expect_success 'detach if the same branch is already checked out' '
+ (
+ cd here &&
+ git checkout newmaster &&
+ test_must_fail git symbolic-ref HEAD
+ )
+'
+
test_done
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 25/28] prune: strategies for linked checkouts
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (23 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 24/28] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-09 8:20 ` Eric Sunshine
2014-03-08 2:48 ` [PATCH v5 26/28] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
` (4 subsequent siblings)
29 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
(alias R=$GIT_COMMON_DIR/repos/<id>)
- linked checkouts are supposed to keep its location in $R/gitdir up
to date. The use case is auto fixup after a manual checkout move.
- linked checkouts are supposed to update mtime of $R/gitdir. If
$R/gitdir's mtime is older than a limit, and it points to nowhere,
repos/<id> is to be pruned.
- If $R/locked exists, repos/<id> is not supposed to be pruned. If
$R/locked exists and $R/gitdir's mtime is older than a really long
limit, warn about old unused repo.
- "git checkout --to" is supposed to make a hard link named $R/link
pointing to the .git file on supported file systems to help detect
the user manually deleting the checkout. If $R/link exists and its
link count is greated than 1, the repo is kept.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-prune.txt | 3 ++
Documentation/gitrepository-layout.txt | 19 +++++++++
builtin/checkout.c | 14 +++++++
builtin/prune.c | 74 ++++++++++++++++++++++++++++++++++
setup.c | 13 ++++++
5 files changed, 123 insertions(+)
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 058ac0d..7babf11 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -48,6 +48,9 @@ OPTIONS
--expire <time>::
Only expire loose objects older than <time>.
+--repos::
+ Prune directories in $GIT_DIR/repos.
+
<head>...::
In addition to objects
reachable from any of our references, keep objects
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 2206f35..dff6c47 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -252,6 +252,25 @@ repos::
$GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/repos" will be
used instead.
+repos/<id>/gitdir::
+ A text file containing the absolute path back to the .git file
+ that points to here. This is used to check if the linked
+ repository has been manually removed and there is no need to
+ keep this directory any more. mtime of this file should be
+ updated every time the linked repository is accessed.
+
+repos/<id>/locked::
+ If this file exists, the linked repository may be on a
+ portable device and not available. It does not mean that the
+ linked repository is gone and `repos/<id>` could be
+ removed. The file's content contains a reason string on why
+ the repository is locked.
+
+repos/<id>/link::
+ If this file exists, it is a hard link to the linked .git
+ file. It is used to detect if the linked repository is
+ manually removed.
+
SEE ALSO
--------
linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 598b43d..9dc80f1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -899,12 +899,22 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
junk_git_dir = sb_repo.buf;
is_junk = 1;
+ /*
+ * lock the incomplete repo so prunt won't delete it, unlock
+ * after the preparation is over.
+ */
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ write_file(sb.buf, 1, "initializing\n");
+
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
junk_work_tree = path;
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+ write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
real_path(get_git_common_dir()), name);
/*
@@ -913,6 +923,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
* value would do because this value will be ignored and
* replaced at the next (real) checkout.
*/
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
strbuf_reset(&sb);
@@ -931,6 +942,9 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
ret = run_command(&cp);
if (!ret)
is_junk = 0;
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ unlink_or_warn(sb.buf);
strbuf_release(&sb);
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
diff --git a/builtin/prune.c b/builtin/prune.c
index de43b26..733cb3b 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -112,6 +112,70 @@ static void prune_object_dir(const char *path)
}
}
+static const char *prune_repo_dir(const char *id, struct stat *st)
+{
+ char *path;
+ int fd, len;
+ if (file_exists(git_path("repos/%s/locked", id)))
+ return NULL;
+ if (stat(git_path("repos/%s/gitdir", id), st)) {
+ st->st_mtime = expire;
+ return _("gitdir does not exist");
+ }
+ fd = open(git_path("repos/%s/gitdir", id), O_RDONLY);
+ len = st->st_size;
+ path = xmalloc(len + 1);
+ read_in_full(fd, path, len);
+ close(fd);
+ while (path[len - 1] == '\n' || path[len - 1] == '\r')
+ len--;
+ path[len] = '\0';
+ if (!file_exists(path)) {
+ struct stat st_link;
+ free(path);
+ /*
+ * the repo is moved manually and has not been
+ * accessed since?
+ */
+ if (!stat(git_path("repos/%s/link", id), &st_link) &&
+ st_link.st_nlink > 1)
+ return NULL;
+ return _("gitdir points to non-existing file");
+ }
+ free(path);
+ return NULL;
+}
+
+static void prune_repos_dir(void)
+{
+ const char *reason;
+ DIR *dir = opendir(git_path("repos"));
+ struct dirent *d;
+ int removed = 0;
+ struct stat st;
+ if (!dir)
+ return;
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ if ((reason = prune_repo_dir(d->d_name, &st)) != NULL &&
+ st.st_mtime <= expire) {
+ struct strbuf sb = STRBUF_INIT;
+ if (show_only || verbose)
+ printf(_("Removing repos/%s: %s\n"), d->d_name, reason);
+ if (show_only)
+ continue;
+ strbuf_addstr(&sb, git_path("repos/%s", d->d_name));
+ remove_dir_recursively(&sb, 0);
+ strbuf_release(&sb);
+ removed = 1;
+ }
+ }
+ closedir(dir);
+ if (removed)
+ rmdir(git_path("repos"));
+}
+
/*
* Write errors (particularly out of space) can result in
* failed temporary packs (and more rarely indexes and other
@@ -138,10 +202,12 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct progress *progress = NULL;
+ int prune_repos = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_BOOL(0, "repos", &prune_repos, N_("prune .git/repos/")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
OPT_END()
@@ -154,6 +220,14 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
init_revisions(&revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+
+ if (prune_repos) {
+ if (argc)
+ die(_("--repos does not take extra arguments"));
+ prune_repos_dir();
+ return 0;
+ }
+
while (argc--) {
unsigned char sha1[20];
const char *name = *argv++;
diff --git a/setup.c b/setup.c
index 40ce191..5529b26 100644
--- a/setup.c
+++ b/setup.c
@@ -336,6 +336,17 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
return ret;
}
+static void update_linked_gitdir(const char *gitfile, const char *gitdir)
+{
+ struct strbuf path = STRBUF_INIT;
+ struct stat st;
+
+ strbuf_addf(&path, "%s/gitfile", gitdir);
+ if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
+ write_file(path.buf, 0, "%s\n", gitfile);
+ strbuf_release(&path);
+}
+
/*
* Try to read the location of the git directory from the .git file,
* return path to git directory if found.
@@ -384,6 +395,8 @@ const char *read_gitfile(const char *path)
if (!is_git_directory(dir))
die("Not a git repository: %s", dir);
+
+ update_linked_gitdir(path, dir);
path = real_path(dir);
free(buf);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v5 25/28] prune: strategies for linked checkouts
2014-03-08 2:48 ` [PATCH v5 25/28] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
@ 2014-03-09 8:20 ` Eric Sunshine
0 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-09 8:20 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Fri, Mar 7, 2014 at 9:48 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> (alias R=$GIT_COMMON_DIR/repos/<id>)
>
> - linked checkouts are supposed to keep its location in $R/gitdir up
> to date. The use case is auto fixup after a manual checkout move.
>
> - linked checkouts are supposed to update mtime of $R/gitdir. If
> $R/gitdir's mtime is older than a limit, and it points to nowhere,
> repos/<id> is to be pruned.
>
> - If $R/locked exists, repos/<id> is not supposed to be pruned. If
> $R/locked exists and $R/gitdir's mtime is older than a really long
> limit, warn about old unused repo.
>
> - "git checkout --to" is supposed to make a hard link named $R/link
> pointing to the .git file on supported file systems to help detect
> the user manually deleting the checkout. If $R/link exists and its
> link count is greated than 1, the repo is kept.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 598b43d..9dc80f1 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -899,12 +899,22 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
> junk_git_dir = sb_repo.buf;
> is_junk = 1;
>
> + /*
> + * lock the incomplete repo so prunt won't delete it, unlock
s/prunt/prune/
> + * after the preparation is over.
> + */
> + strbuf_addf(&sb, "%s/locked", sb_repo.buf);
> + write_file(sb.buf, 1, "initializing\n");
> +
> strbuf_addf(&sb_git, "%s/.git", path);
> if (safe_create_leading_directories_const(sb_git.buf))
> die_errno(_("could not create leading directories of '%s'"),
> sb_git.buf);
> junk_work_tree = path;
>
> + strbuf_reset(&sb);
> + strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
> + write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
> write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
> real_path(get_git_common_dir()), name);
> /*
^ permalink raw reply [flat|nested] 169+ messages in thread
* [PATCH v5 26/28] gc: style change -- no SP before closing bracket
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (24 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 25/28] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 27/28] gc: support prune --repos Nguyễn Thái Ngọc Duy
` (3 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/gc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index c19545d..39d9b27 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -260,7 +260,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("suppress progress reporting")),
{ OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
N_("prune unreferenced objects"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
+ PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire},
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")),
OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")),
@@ -273,7 +273,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
- argv_array_pushl(&prune, "prune", "--expire", NULL );
+ argv_array_pushl(&prune, "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
git_config(gc_config, NULL);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 27/28] gc: support prune --repos
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (25 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 26/28] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-08 2:48 ` [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
` (2 subsequent siblings)
29 siblings, 0 replies; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 7 +++++++
builtin/gc.c | 17 +++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 313d4b3..c635b8d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1183,6 +1183,13 @@ gc.pruneexpire::
"now" may be used to disable this grace period and always prune
unreachable objects immediately.
+gc.prunereposexpire::
+ When 'git gc' is run, it will call
+ 'prune --repos --expire 3.months.ago'.
+ Override the grace period with this config variable. The value
+ "now" may be used to disable the grace period and always prune
+ $GIT_DIR/repos immediately.
+
gc.reflogexpire::
gc.<pattern>.reflogexpire::
'git reflog expire' removes reflog entries older than
diff --git a/builtin/gc.c b/builtin/gc.c
index 39d9b27..85c3c0c 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -30,11 +30,13 @@ static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static const char *prune_expire = "2.weeks.ago";
+static const char *prune_repos_expire = "3.months.ago";
static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
static struct argv_array reflog = ARGV_ARRAY_INIT;
static struct argv_array repack = ARGV_ARRAY_INIT;
static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array prune_repos = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
static char *pidfile;
@@ -81,6 +83,14 @@ static int gc_config(const char *var, const char *value, void *cb)
}
return git_config_string(&prune_expire, var, value);
}
+ if (!strcmp(var, "gc.prunereposexpire")) {
+ if (value && strcmp(value, "now")) {
+ unsigned long now = approxidate("now");
+ if (approxidate(value) >= now)
+ return error(_("Invalid %s: '%s'"), var, value);
+ }
+ return git_config_string(&prune_repos_expire, var, value);
+ }
return git_default_config(var, value, cb);
}
@@ -274,6 +284,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
argv_array_pushl(&prune, "prune", "--expire", NULL);
+ argv_array_pushl(&prune_repos, "prune", "--repos", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
git_config(gc_config, NULL);
@@ -334,6 +345,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
return error(FAILED_RUN, prune.argv[0]);
}
+ if (prune_repos_expire) {
+ argv_array_push(&prune_repos, prune_repos_expire);
+ if (run_command_v_opt(prune_repos.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, prune_repos.argv[0]);
+ }
+
if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
return error(FAILED_RUN, rerere.argv[0]);
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (26 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 27/28] gc: support prune --repos Nguyễn Thái Ngọc Duy
@ 2014-03-08 2:48 ` Nguyễn Thái Ngọc Duy
2014-03-09 8:21 ` Eric Sunshine
2014-03-09 8:18 ` [PATCH v5 00/28] Support multiple checkouts Eric Sunshine
2014-07-06 20:46 ` Max Kirillov
29 siblings, 1 reply; 169+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2014-03-08 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In linked checkouts, borrowed parts like config is taken from
$GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as
garbage.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/count-objects.c | 4 +++-
cache.h | 1 +
path.c | 29 +++++++++++++++++++++++++++--
3 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index a7f70cb..d3a1620 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -102,8 +102,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
/* we do not take arguments other than flags for now */
if (argc)
usage_with_options(count_objects_usage, opts);
- if (verbose)
+ if (verbose) {
report_garbage = real_report_garbage;
+ report_linked_checkout_garbage();
+ }
memcpy(path, objdir, len);
if (len && objdir[len-1] != '/')
path[len++] = '/';
diff --git a/cache.h b/cache.h
index 979ac6e..a0ff430 100644
--- a/cache.h
+++ b/cache.h
@@ -662,6 +662,7 @@ extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1
extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern const char *git_path_submodule(const char *path, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+extern void report_linked_checkout_garbage(void);
extern char *sha1_file_name(const unsigned char *sha1);
extern char *sha1_pack_name(const unsigned char *sha1);
diff --git a/path.c b/path.c
index ddb5962..5a7dc45 100644
--- a/path.c
+++ b/path.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "strbuf.h"
#include "string-list.h"
+#include "dir.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -91,9 +92,9 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
}
static const char *common_list[] = {
- "/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules",
+ "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules",
"/objects", "/refs", "/remotes", "/repos", "/rr-cache", "/svn",
- "config", "gc.pid", "packed-refs", "shallow",
+ "config", "!gc.pid", "packed-refs", "shallow",
NULL
};
@@ -107,6 +108,8 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
for (p = common_list; *p; p++) {
const char *path = *p;
int is_dir = 0;
+ if (*path == '!')
+ path++;
if (*path == '/') {
path++;
is_dir = 1;
@@ -122,6 +125,28 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
}
}
+void report_linked_checkout_garbage(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char **p;
+ int len;
+
+ if (!git_common_dir_env)
+ return;
+ strbuf_addf(&sb, "%s/", get_git_dir());
+ len = sb.len;
+ for (p = common_list; *p; p++) {
+ const char *path = *p;
+ if (*path == '!')
+ continue;
+ strbuf_setlen(&sb, len);
+ strbuf_addstr(&sb, path);
+ if (file_exists(sb.buf))
+ report_garbage("unused in linked checkout", sb.buf);
+ }
+ strbuf_release(&sb);
+}
+
static void adjust_git_path(struct strbuf *buf, int git_dir_len)
{
const char *base = buf->buf + git_dir_len;
--
1.9.0.40.gaa8c3ea
^ permalink raw reply related [flat|nested] 169+ messages in thread
* Re: [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-08 2:48 ` [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
@ 2014-03-09 8:21 ` Eric Sunshine
2014-03-09 11:25 ` Duy Nguyen
0 siblings, 1 reply; 169+ messages in thread
From: Eric Sunshine @ 2014-03-09 8:21 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Fri, Mar 7, 2014 at 9:48 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> In linked checkouts, borrowed parts like config is taken from
> $GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as
> garbage.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/path.c b/path.c
> index ddb5962..5a7dc45 100644
> --- a/path.c
> +++ b/path.c
> @@ -4,6 +4,7 @@
> #include "cache.h"
> #include "strbuf.h"
> #include "string-list.h"
> +#include "dir.h"
>
> static int get_st_mode_bits(const char *path, int *mode)
> {
> @@ -91,9 +92,9 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
> }
Do you want to add a comment here explaining what the "!" prefix on
some entries means, or is it sufficiently self-evident to anyone
looking at the consumers of this array?
> static const char *common_list[] = {
> - "/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules",
> + "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules",
> "/objects", "/refs", "/remotes", "/repos", "/rr-cache", "/svn",
> - "config", "gc.pid", "packed-refs", "shallow",
> + "config", "!gc.pid", "packed-refs", "shallow",
> NULL
> };
>
> @@ -107,6 +108,8 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
> for (p = common_list; *p; p++) {
> const char *path = *p;
> int is_dir = 0;
> + if (*path == '!')
> + path++;
> if (*path == '/') {
> path++;
> is_dir = 1;
> @@ -122,6 +125,28 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
> }
> }
>
> +void report_linked_checkout_garbage(void)
> +{
> + struct strbuf sb = STRBUF_INIT;
> + const char **p;
> + int len;
> +
> + if (!git_common_dir_env)
> + return;
> + strbuf_addf(&sb, "%s/", get_git_dir());
> + len = sb.len;
> + for (p = common_list; *p; p++) {
> + const char *path = *p;
> + if (*path == '!')
> + continue;
> + strbuf_setlen(&sb, len);
> + strbuf_addstr(&sb, path);
> + if (file_exists(sb.buf))
> + report_garbage("unused in linked checkout", sb.buf);
> + }
> + strbuf_release(&sb);
> +}
> +
> static void adjust_git_path(struct strbuf *buf, int git_dir_len)
> {
> const char *base = buf->buf + git_dir_len;
> --
> 1.9.0.40.gaa8c3ea
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/...
2014-03-09 8:21 ` Eric Sunshine
@ 2014-03-09 11:25 ` Duy Nguyen
0 siblings, 0 replies; 169+ messages in thread
From: Duy Nguyen @ 2014-03-09 11:25 UTC (permalink / raw)
To: Eric Sunshine; +Cc: Git List, Junio C Hamano
On Sun, Mar 9, 2014 at 3:21 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Fri, Mar 7, 2014 at 9:48 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> In linked checkouts, borrowed parts like config is taken from
>> $GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as
>> garbage.
>>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> diff --git a/path.c b/path.c
>> index ddb5962..5a7dc45 100644
>> --- a/path.c
>> +++ b/path.c
>> @@ -4,6 +4,7 @@
>> #include "cache.h"
>> #include "strbuf.h"
>> #include "string-list.h"
>> +#include "dir.h"
>>
>> static int get_st_mode_bits(const char *path, int *mode)
>> {
>> @@ -91,9 +92,9 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
>> }
>
> Do you want to add a comment here explaining what the "!" prefix on
> some entries means, or is it sufficiently self-evident to anyone
> looking at the consumers of this array?
I was hoping it was clear from the patch how this "!" was used (or "/"
in the patch that introduces common_list[]). But if any reader thinks
otherwise, I'm happy to add a comment.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 00/28] Support multiple checkouts
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (27 preceding siblings ...)
2014-03-08 2:48 ` [PATCH v5 28/28] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
@ 2014-03-09 8:18 ` Eric Sunshine
2014-07-06 20:46 ` Max Kirillov
29 siblings, 0 replies; 169+ messages in thread
From: Eric Sunshine @ 2014-03-09 8:18 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano
On Fri, Mar 7, 2014 at 9:47 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> The diff against v4 is kinda big but it's mostly about converting
> `...` to $(...) and making git_path() and friends return a const
> string.
>
> Another notable change is I no longer attempt to support checkouts on
> portable devices. Torsten pointed out (privately) that my dealing with
> Windows drives was insufficient. And Junio was not so happy with how
> link() was handled either. We can revisit it later.
>
> Many thanks to Eric, who was very patient to go through the series
> carefully and pointed out problems that I overlooked.
>
> v4..v5 diff below for convenience
Thanks for providing an interdiff between the two versions. It
simplified things considerably, especially with the v4 reviews still
relatively fresh in my brain.
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 00/28] Support multiple checkouts
2014-03-08 2:47 ` [PATCH v5 00/28] Support multiple checkouts Nguyễn Thái Ngọc Duy
` (28 preceding siblings ...)
2014-03-09 8:18 ` [PATCH v5 00/28] Support multiple checkouts Eric Sunshine
@ 2014-07-06 20:46 ` Max Kirillov
2014-07-07 10:25 ` Duy Nguyen
29 siblings, 1 reply; 169+ messages in thread
From: Max Kirillov @ 2014-07-06 20:46 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano
Hi.
What future does this have? Currently it is marked as
"Stalled", but still mergeable with some trivial conflicts
and seem to be working (except some bugs in interaction with
submodules, see below). It would be very nice if this
feature is officially supported.
I also have a comment about how it interacts with submodules.
Would it be more appropriate to mark "modules" as a
per-checkout directory? Because each of the working tree's
submodule is obviously a separated directory in filesystem,
and in most cases (at least in my practice) they are
checked-out to different revisions.
So, currently (before proper linking of submodules checkouts
implemented), if make submodules per-checkout (actually it
appears to somehow work even with current code, maybe
because some submodule code ignores the common_dir), one
could run "git submodule update" if necessary, and get fully
separated clones, which would work normally.
It still may break if submodules are removed, added or
renamed, but this seems to be inevitable until config is
separated to per-checkout and common parts, which I suppose
is a much bigger task.
--
Max
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 00/28] Support multiple checkouts
2014-07-06 20:46 ` Max Kirillov
@ 2014-07-07 10:25 ` Duy Nguyen
2014-07-07 10:49 ` Dennis Kaarsemaker
0 siblings, 1 reply; 169+ messages in thread
From: Duy Nguyen @ 2014-07-07 10:25 UTC (permalink / raw)
To: Max Kirillov; +Cc: Git Mailing List, Junio C Hamano, Dennis Kaarsemaker
On Mon, Jul 7, 2014 at 3:46 AM, Max Kirillov <max@max630.net> wrote:
> Hi.
>
> What future does this have? Currently it is marked as
> "Stalled", but still mergeable with some trivial conflicts
> and seem to be working (except some bugs in interaction with
> submodules, see below). It would be very nice if this
> feature is officially supported.
It's to be re-rolled soon. I have a patch about sparse-checkout and
Dennis may contribute another one to enable "checkout --to" from a
bare repository. By the way Dennis has been testing this feature and
he reported (off-list) it worked fine, which is good news. I have done
nothing so far because my git time (or energy to be precise) is
limited these days, and I wanted to see if Dennis reported any new
bugs.
> I also have a comment about how it interacts with submodules.
> Would it be more appropriate to mark "modules" as a
> per-checkout directory? Because each of the working tree's
> submodule is obviously a separated directory in filesystem,
> and in most cases (at least in my practice) they are
> checked-out to different revisions.
Submodule interaction is something I have avoided so far because I'm
not a big user and admittedly does not follow its development closely.
I think we could get this in first, then let submodule people aware
about this feature and let them decide how to use it.
> So, currently (before proper linking of submodules checkouts
> implemented), if make submodules per-checkout (actually it
> appears to somehow work even with current code, maybe
> because some submodule code ignores the common_dir), one
> could run "git submodule update" if necessary, and get fully
> separated clones, which would work normally.
>
> It still may break if submodules are removed, added or
> renamed, but this seems to be inevitable until config is
> separated to per-checkout and common parts, which I suppose
> is a much bigger task.
--
Duy
^ permalink raw reply [flat|nested] 169+ messages in thread
* Re: [PATCH v5 00/28] Support multiple checkouts
2014-07-07 10:25 ` Duy Nguyen
@ 2014-07-07 10:49 ` Dennis Kaarsemaker
2014-07-07 17:32 ` Max Kirillov
0 siblings, 1 reply; 169+ messages in thread
From: Dennis Kaarsemaker @ 2014-07-07 10:49 UTC (permalink / raw)
To: Duy Nguyen; +Cc: Max Kirillov, Git Mailing List, Junio C Hamano
On ma, 2014-07-07 at 17:25 +0700, Duy Nguyen wrote:
> > I also have a comment about how it interacts with submodules.
> > Would it be more appropriate to mark "modules" as a
> > per-checkout directory? Because each of the working tree's
> > submodule is obviously a separated directory in filesystem,
> > and in most cases (at least in my practice) they are
> > checked-out to different revisions.
>
> Submodule interaction is something I have avoided so far because I'm
> not a big user and admittedly does not follow its development closely.
> I think we could get this in first, then let submodule people aware
> about this feature and let them decide how to use it.
I do intend to use checkout --to and submodule update on the same
repository, but have not yet done so. I will poke at that later this
month. If you can easily reproduce errors, I would appreciate to know
how, because my use of submodules is very limited.
--
Dennis Kaarsemaker
http://www.kaarsemaker.net
^ permalink raw reply [flat|nested] 169+ messages in thread