summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2017-03-22 20:32:55 +0000
committerGitHub <noreply@github.com>2017-03-22 20:32:55 +0000
commitf623cf894a7e2591e7b4424b24143d1a7584cf9e (patch)
treeeff28ba6a1a91f51fb71a07969e688fcf1ecc8cd /src
parent6fd6c67824d2caad9f8716a676820d8319f79a9d (diff)
parentb0c9bc920fabfd814946d555738ac7ba042154d7 (diff)
downloadlibgit2-f623cf894a7e2591e7b4424b24143d1a7584cf9e.tar.gz
Merge pull request #4163 from pks-t/pks/submodules-with-worktrees
Worktree fixes
Diffstat (limited to 'src')
-rw-r--r--src/refdb_fs.c10
-rw-r--r--src/submodule.c24
-rw-r--r--src/worktree.c148
-rw-r--r--src/worktree.h2
4 files changed, 135 insertions, 49 deletions
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 450b3f3ec..b325d2763 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -739,6 +739,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
{
int error, filebuf_flags;
git_buf ref_path = GIT_BUF_INIT;
+ const char *basedir;
assert(file && backend && name);
@@ -747,13 +748,18 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
return GIT_EINVALIDSPEC;
}
+ if (is_per_worktree_ref(name))
+ basedir = backend->gitpath;
+ else
+ basedir = backend->commonpath;
+
/* Remove a possibly existing empty directory hierarchy
* which name would collide with the reference name
*/
- if ((error = git_futils_rmdir_r(name, backend->gitpath, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
+ if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
return error;
- if (git_buf_joinpath(&ref_path, backend->gitpath, name) < 0)
+ if (git_buf_joinpath(&ref_path, basedir, name) < 0)
return -1;
filebuf_flags = GIT_FILEBUF_FORCE;
diff --git a/src/submodule.c b/src/submodule.c
index 191cdf3dd..ddd4b0663 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -22,6 +22,7 @@
#include "iterator.h"
#include "path.h"
#include "index.h"
+#include "worktree.h"
#define GIT_MODULES_FILE ".gitmodules"
@@ -2038,17 +2039,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo)
static int get_url_base(git_buf *url, git_repository *repo)
{
int error;
+ git_worktree *wt = NULL;
git_remote *remote = NULL;
- if (!(error = lookup_default_remote(&remote, repo))) {
+ if ((error = lookup_default_remote(&remote, repo)) == 0) {
error = git_buf_sets(url, git_remote_url(remote));
- git_remote_free(remote);
- }
- else if (error == GIT_ENOTFOUND) {
- /* if repository does not have a default remote, use workdir instead */
+ goto out;
+ } else if (error != GIT_ENOTFOUND)
+ goto out;
+ else
giterr_clear();
+
+ /* if repository does not have a default remote, use workdir instead */
+ if (git_repository_is_worktree(repo)) {
+ if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
+ goto out;
+ error = git_buf_sets(url, wt->parent_path);
+ } else
error = git_buf_sets(url, git_repository_workdir(repo));
- }
+
+out:
+ git_remote_free(remote);
+ git_worktree_free(wt);
return error;
}
diff --git a/src/worktree.c b/src/worktree.c
index 5abc98945..393a088fe 100644
--- a/src/worktree.c
+++ b/src/worktree.c
@@ -14,11 +14,20 @@
#include "repository.h"
#include "worktree.h"
-static bool is_worktree_dir(git_buf *dir)
+static bool is_worktree_dir(const char *dir)
{
- return git_path_contains_file(dir, "commondir")
- && git_path_contains_file(dir, "gitdir")
- && git_path_contains_file(dir, "HEAD");
+ git_buf buf = GIT_BUF_INIT;
+ int error;
+
+ if (git_buf_sets(&buf, dir) < 0)
+ return -1;
+
+ error = git_path_contains_file(&buf, "commondir")
+ && git_path_contains_file(&buf, "gitdir")
+ && git_path_contains_file(&buf, "HEAD");
+
+ git_buf_free(&buf);
+ return error;
}
int git_worktree_list(git_strarray *wts, git_repository *repo)
@@ -47,7 +56,7 @@ int git_worktree_list(git_strarray *wts, git_repository *repo)
git_buf_truncate(&path, len);
git_buf_puts(&path, worktree);
- if (!is_worktree_dir(&path)) {
+ if (!is_worktree_dir(path.ptr)) {
git_vector_remove(&worktrees, i);
git__free(worktree);
}
@@ -112,6 +121,46 @@ out:
return err;
}
+static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name)
+{
+ git_buf gitdir = GIT_BUF_INIT;
+ git_worktree *wt = NULL;
+ int error = 0;
+
+ if (!is_worktree_dir(dir)) {
+ error = -1;
+ goto out;
+ }
+
+ if ((wt = git__calloc(1, sizeof(struct git_repository))) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ if ((wt->name = git__strdup(name)) == NULL
+ || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL
+ || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL
+ || (wt->parent_path = git__strdup(parent)) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0)
+ goto out;
+ wt->gitdir_path = git_buf_detach(&gitdir);
+
+ wt->locked = !!git_worktree_is_locked(NULL, wt);
+
+ *out = wt;
+
+out:
+ if (error)
+ git_worktree_free(wt);
+ git_buf_free(&gitdir);
+
+ return error;
+}
+
int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name)
{
git_buf path = GIT_BUF_INIT;
@@ -125,33 +174,47 @@ int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *na
if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0)
goto out;
- if (!is_worktree_dir(&path)) {
- error = -1;
+ if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
goto out;
- }
- if ((wt = git__malloc(sizeof(struct git_repository))) == NULL) {
+out:
+ git_buf_free(&path);
+
+ if (error)
+ git_worktree_free(wt);
+
+ return error;
+}
+
+int git_worktree_open_from_repository(git_worktree **out, git_repository *repo)
+{
+ git_buf parent = GIT_BUF_INIT;
+ const char *gitdir, *commondir;
+ char *name = NULL;
+ int error = 0;
+
+ if (!git_repository_is_worktree(repo)) {
+ giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo");
error = -1;
goto out;
}
- if ((wt->name = git__strdup(name)) == NULL
- || (wt->commondir_path = git_worktree__read_link(path.ptr, "commondir")) == NULL
- || (wt->gitlink_path = git_worktree__read_link(path.ptr, "gitdir")) == NULL
- || (wt->parent_path = git__strdup(git_repository_path(repo))) == NULL) {
- error = -1;
+ gitdir = git_repository_path(repo);
+ commondir = git_repository_commondir(repo);
+
+ if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0)
goto out;
- }
- wt->gitdir_path = git_buf_detach(&path);
- wt->locked = !!git_worktree_is_locked(NULL, wt);
- (*out) = wt;
+ /* The name is defined by the last component in '.git/worktree/%s' */
+ name = git_path_basename(gitdir);
-out:
- git_buf_free(&path);
+ if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0)
+ goto out;
+out:
if (error)
- git_worktree_free(wt);
+ free(name);
+ git_buf_free(&parent);
return error;
}
@@ -177,7 +240,7 @@ int git_worktree_validate(const git_worktree *wt)
assert(wt);
git_buf_puts(&buf, wt->gitdir_path);
- if (!is_worktree_dir(&buf)) {
+ if (!is_worktree_dir(buf.ptr)) {
giterr_set(GITERR_WORKTREE,
"Worktree gitdir ('%s') is not valid",
wt->gitlink_path);
@@ -209,7 +272,7 @@ out:
int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree)
{
- git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
+ git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT;
git_reference *ref = NULL, *head = NULL;
git_commit *commit = NULL;
git_repository *wt = NULL;
@@ -220,35 +283,39 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name,
*out = NULL;
- /* Create worktree related files in commondir */
- if ((err = git_buf_joinpath(&path, repo->commondir, "worktrees")) < 0)
+ /* Create gitdir directory ".git/worktrees/<name>" */
+ if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0)
goto out;
- if (!git_path_exists(path.ptr))
- if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+ if (!git_path_exists(gitdir.ptr))
+ if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
goto out;
- if ((err = git_buf_joinpath(&path, path.ptr, name)) < 0)
+ if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0)
+ goto out;
+ if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
goto out;
- if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+ if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0)
goto out;
/* Create worktree work dir */
if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0)
goto out;
+ if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0)
+ goto out;
/* Create worktree .git file */
- if ((err = git_buf_printf(&buf, "gitdir: %s\n", path.ptr)) < 0)
+ if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0)
goto out;
- if ((err = write_wtfile(worktree, ".git", &buf)) < 0)
+ if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0)
goto out;
- /* Create commondir files */
- if ((err = git_buf_sets(&buf, repo->commondir)) < 0
+ /* Create gitdir files */
+ if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0)
|| (err = git_buf_putc(&buf, '\n')) < 0
- || (err = write_wtfile(path.ptr, "commondir", &buf)) < 0)
+ || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0)
goto out;
- if ((err = git_buf_joinpath(&buf, worktree, ".git")) < 0
+ if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0
|| (err = git_buf_putc(&buf, '\n')) < 0
- || (err = write_wtfile(path.ptr, "gitdir", &buf)) < 0)
+ || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
goto out;
/* Create new branch */
@@ -260,9 +327,9 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name,
goto out;
/* Set worktree's HEAD */
- if ((err = git_repository_create_head(path.ptr, name)) < 0)
+ if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
goto out;
- if ((err = git_repository_open(&wt, worktree)) < 0)
+ if ((err = git_repository_open(&wt, wddir.ptr)) < 0)
goto out;
/* Checkout worktree's HEAD */
@@ -275,7 +342,8 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name,
goto out;
out:
- git_buf_free(&path);
+ git_buf_free(&gitdir);
+ git_buf_free(&wddir);
git_buf_free(&buf);
git_reference_free(ref);
git_reference_free(head);
@@ -394,7 +462,7 @@ int git_worktree_prune(git_worktree *wt, unsigned flags)
}
/* Delete gitdir in parent repository */
- if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->parent_path, wt->name)) < 0)
+ if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0)
goto out;
if (!git_path_exists(path.ptr))
{
diff --git a/src/worktree.h b/src/worktree.h
index b8e527968..57c2e65f0 100644
--- a/src/worktree.h
+++ b/src/worktree.h
@@ -24,7 +24,7 @@ struct git_worktree {
/* Path to the common directory contained in the parent
* repository */
char *commondir_path;
- /* Path to the parent's .git directory */
+ /* Path to the parent's working directory */
char *parent_path;
int locked:1;