* [PATCHv3 5/5] Add test showing git-fetch groks gitfiles
From: Phil Hord @ 2011-10-05 13:35 UTC (permalink / raw)
To: git@vger.kernel.org, Phil Hord
Cc: Junio C Hamano, Erik Faye-Lund, Nguyen Thai Ngoc Duy
Add a test for two subtly different cases: 'git fetch path/.git'
and 'git fetch path' to confirm that transport recognizes both
paths as git repositories when using the gitfile mechanism.
Signed-off-by: Phil Hord <hordp@cisco.com>
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e810314..87ee016 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -206,6 +206,20 @@ test_expect_success 'clone from .git file' '
git clone dst/.git dst2
'
+test_expect_success 'fetch from .git gitfile' '
+ (
+ cd dst2 &&
+ git fetch ../dst/.git
+ )
+'
+
+test_expect_success 'fetch from gitfile parent' '
+ (
+ cd dst2 &&
+ git fetch ../dst
+ )
+'
+
test_expect_success 'clone separate gitdir where target already exists' '
rm -rf dst &&
test_must_fail git clone --separate-git-dir realgitdir src dst
--
1.7.7.505.ga09f6
^ permalink raw reply related
* [PATCHv3 4/5] Teach transport about the gitfile mechanism
From: Phil Hord @ 2011-10-05 13:35 UTC (permalink / raw)
To: git@vger.kernel.org, Phil Hord
Cc: Junio C Hamano, Erik Faye-Lund, Nguyen Thai Ngoc Duy
The transport_get() function assumes that a regular file is a
bundle rather than a local git directory. Check if the file is
actually a gitfile. If so, do not try to process it as a
bundle, but treat it as a local repository instead.
Signed-off-by: Phil Hord <hordp@cisco.com>
diff --git a/transport.c b/transport.c
index cd49a25..2ff2d68 100644
--- a/transport.c
+++ b/transport.c
@@ -915,7 +915,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
ret->fetch = fetch_objs_via_rsync;
ret->push = rsync_transport_push;
ret->smart_options = NULL;
- } else if (is_local(url) && is_file(url)) {
+ } else if (is_local(url) && is_file(url) && !is_gitfile(url)) {
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
ret->data = data;
ret->get_refs_list = get_refs_from_bundle;
--
1.7.7.505.ga09f6
^ permalink raw reply related
* [PATCHv3 3/5] Add a common is_gitfile function
From: Phil Hord @ 2011-10-05 13:33 UTC (permalink / raw)
To: git@vger.kernel.org, Phil Hord
Cc: Junio C Hamano, Erik Faye-Lund, Nguyen Thai Ngoc Duy
There are at least two locations in the code that check for
gitfiles. Each one is slightly different for pretty good
reasons. Work towards a common API by abstracting the
"is this a gitfile" question into a single function.
Signed-off-by: Phil Hord <hordp@cisco.com>
diff --git a/builtin/clone.c b/builtin/clone.c
index 488f48e..5110399 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -120,13 +120,7 @@ static char *get_repo_path(const char *repo, int *is_bundle)
return xstrdup(absolute_path(path));
} else if (S_ISREG(st.st_mode) && st.st_size > 8) {
/* Is it a "gitfile"? */
- char signature[8];
- int len, fd = open(path, O_RDONLY);
- if (fd < 0)
- continue;
- len = read_in_full(fd, signature, 8);
- close(fd);
- if (len != 8 || strncmp(signature, "gitdir: ", 8))
+ if (!is_gitfile(path))
continue;
path = read_gitfile(path);
if (path) {
diff --git a/cache.h b/cache.h
index 7eeb8cf..61e1b0f 100644
--- a/cache.h
+++ b/cache.h
@@ -441,6 +441,7 @@ extern const char *get_git_work_tree(void);
extern const char *read_gitfile(const char *path);
extern const char *resolve_gitdir(const char *suspect);
extern void set_git_work_tree(const char *tree);
+extern int is_gitfile(const char *path);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
diff --git a/setup.c b/setup.c
index 61c22e6..a3d5a41 100644
--- a/setup.c
+++ b/setup.c
@@ -358,7 +358,7 @@ const char *read_gitfile(const char *path)
if (stat(path, &st))
return NULL;
- if (!S_ISREG(st.st_mode))
+ if (!is_gitfile(path))
return NULL;
fd = open(path, O_RDONLY);
if (fd < 0)
@@ -368,9 +368,6 @@ const char *read_gitfile(const char *path)
close(fd);
if (len != st.st_size)
die("Error reading %s", path);
- buf[len] = '\0';
- if (prefixcmp(buf, "gitdir: "))
- die("Invalid gitfile format: %s", path);
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
len--;
if (len < 9)
@@ -397,6 +394,32 @@ const char *read_gitfile(const char *path)
return path;
}
+/*
+ * See if the referenced file looks like a 'gitfile'.
+ * Does not try to determine if the referenced gitdir is actually valid.
+ */
+int is_gitfile(const char *path)
+{
+ struct stat st;
+ char buf[9];
+ int fd, len;
+ if (stat(path, &st))
+ return 0;
+ if (!S_ISREG(st.st_mode))
+ return 0;
+ if (st.st_size < 10 || st.st_size > PATH_MAX)
+ return 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ len = read_in_full(fd, buf, sizeof(buf));
+ close(fd);
+ if (len != sizeof(buf))
+ return 0;
+ return !prefixcmp(buf, "gitdir: ");
+}
+
static const char *setup_explicit_git_dir(const char *gitdirenv,
char *cwd, int len,
int *nongit_ok)
--
1.7.7.505.ga09f6
^ permalink raw reply related
* [PATCHv3 2/5] Learn to handle gitfiles in enter_repo
From: Phil Hord @ 2011-10-05 13:31 UTC (permalink / raw)
To: git@vger.kernel.org, Phil Hord
Cc: Junio C Hamano, Erik Faye-Lund, Nguyen Thai Ngoc Duy
The enter_repo() function is used to navigate into a .git
directory. It knows how to find standard alternatives (DWIM) but
it doesn't handle gitfiles created by git init --separate-git-dir.
This means that git-fetch and others do not work with repositories
using the separate-git-dir mechanism.
Teach enter_repo() to deal with the gitfile mechanism by resolving
the path to the redirected path and continuing tests on that path
instead of the found file.
Signed-off-by: Phil Hord <hordp@cisco.com>
---
Not sure how to resolve this for the 'strict' case.
The actual path followed may be different than the version spelled
out on the input path because of resolved symlinks and whatnot.
This function normally returns the unmolested "original" path
that was validated. In case of a gitfile, I think that means
we should return the url resolved from the gitfile contents.
But should we?
The returned path is only used in git-daemon where it gets
further checks for path restrictions.
A. If we return the gitfile-resolved path, this may cause these
path restrictions to fail since the path gets canonicalized
when the gitfile is created by git.
B. If we do not return the gitfile-resolved path, this can create
a security-hole by allowing remote users to access files they
would otherwise have been restricted from. In effect it creates
an alternate symlink mechanism of which the administator might
not be aware.
diff --git a/path.c b/path.c
index f3d96aa..46ba326 100644
--- a/path.c
+++ b/path.c
@@ -295,6 +295,7 @@ const char *enter_repo(const char *path, int strict)
static const char *suffix[] = {
".git/.git", "/.git", ".git", "", NULL,
};
+ const char *gitfile;
int len = strlen(path);
int i;
while ((1 < len) && (path[len-1] == '/'))
@@ -330,7 +331,12 @@ const char *enter_repo(const char *path, int strict)
break;
}
}
- if (!suffix[i] || chdir(used_path))
+ if (!suffix[i])
+ return NULL;
+ gitfile = read_gitfile(used_path) ;
+ if (gitfile)
+ strcpy(used_path, gitfile);
+ if (chdir(used_path))
return NULL;
path = validated_path;
}
--
1.7.7.505.ga09f6
^ permalink raw reply related
* Re: [RFC/PATCH] Add multiple workdir support to branch/checkout
From: Jay Soffian @ 2011-10-05 13:11 UTC (permalink / raw)
To: Nguyen Thai Ngoc Duy; +Cc: git
In-Reply-To: <CACsJy8AqYq+YF+rvUp=BBeFUAtUz783iF2jbUp3fO58yLp9ptQ@mail.gmail.com>
On Wed, Oct 5, 2011 at 12:02 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:
> Could you please consider a more generic approach? What I have in mind
> is a mechanism to "lock" a branch, so that only commands that have the
> key can update it.
>
> So instead of branch.<name>.checkout, I would have something like
> branch.<name>.locked = <key>, where <key> is just a string. Only
> commands that provide the matching <key> are allowed to update the
> branch. In checkout case, <key> could be "checkout: worktree".
In this case, each workdir needs its own key, so I'd have to record
the key somewhere, unless you meant using a key of "checkout:
</path/to/workdir>".
> This approach addresses more cases than just multiple workdir. We
> could relax restrictions on pushing to a non-bare repository: we only
> disallow pushing to locked branches.
Isn't that another case where you only care if the branch is checked
out and where? So using "branch.<name>.checkout = </path/to/workdir>"
should be fine there too.
> We can also use this to prevent
> users from checking out another branch (by locking HEAD) while in the
> middle of interactive rebase/bisect/...
I dunno, that seems like a really different use case.
j.
^ permalink raw reply
* Re: Git attributes ignored for root directory
From: Michael Haggerty @ 2011-10-05 12:05 UTC (permalink / raw)
To: Gioele Barabucci; +Cc: git
In-Reply-To: <4E8B55FB.1050203@svario.it>
On 10/04/2011 08:52 PM, Gioele Barabucci wrote:
> I just updated to git v1.7.7 using the Ubuntu Lucid PPA and I found that
> `git check-attr` is broken now.
>
> I have this attribute in my `$HOME/.gitattributes` file:
>
> /. show_in_prompt=no
>
> Now, if I go to `$HOME` and run
>
> git check-attr show_in_prompt -- .
>
> With git v1.7.6 this is the answer I got:
>
> .: show_in_prompt: no
>
> With the newer v1.7.7 I get this, instead:
>
> .: show_in_prompt: unspecified
>
> Also, if I use the `--all` option, `check-attr` does not show any
> attribute at all.
>
> I see in the release notes of 1.7.7-rc1 that `check-attr` has been
> changed to allow relative paths to be specified. Maybe this error is
> related to that change.
Indeed, your use case is broken by
f5114a40c0d0276ce6ff215a3dc51eb19da5b420
In fact the support for gitattributes using patterns involving "." was
pretty spotty in v1.7.6 too. For example,
-------------------------------------------
echo ". foo" >./.gitattributes
git check-attr foo -- . ./ ./. x x/ ./x x/.
.: foo: set
./: foo: unspecified WRONG
./.: foo: set
x: foo: unspecified WRONG?
x/: foo: unspecified WRONG?
./x: foo: unspecified WRONG?
x/.: foo: set RIGHT?
-------------------------------------------
echo "/. foo" >./.gitattributes
git check-attr foo -- . ./ ./. x x/ ./x x/.
.: foo: set
./: foo: unspecified WRONG
./.: foo: set
x: foo: unspecified
x/: foo: unspecified
./x: foo: unspecified
x/.: foo: unspecified
-------------------------------------------
echo ". foo" >x/.gitattributes
git check-attr foo -- . ./ ./. x x/ ./x x/.
.: foo: unspecified
./: foo: unspecified
./.: foo: unspecified
x: foo: unspecified WRONG?
x/: foo: unspecified WRONG?
./x: foo: unspecified WRONG?
x/.: foo: set RIGHT?
-------------------------------------------
echo "/. foo" >x/.gitattributes
git check-attr foo -- . ./ ./. x x/ ./x x/.
.: foo: unspecified
./: foo: unspecified
./.: foo: unspecified
x: foo: unspecified WRONG
x/: foo: unspecified WRONG
./x: foo: unspecified WRONG
x/.: foo: set
-------------------------------------------
I conclude that this functionality was never really defined correctly,
and you were pretty lucky that your case worked at all :-)
It's not to hard to fix your particular use case. But for a real fix,
we would need to decide what is the correct behavior in all of the lines
above marked "?"; specifically, should "." match every subdirectory
under a given directory, does it match only the directory containing the
.gitattributes file, or is this construct illegal?
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply
* [PATCH] git-svn: On MSYS, escape and quote SVN_SSH also if set by the user
From: Sebastian Schuberth @ 2011-10-05 9:14 UTC (permalink / raw)
To: git; +Cc: msysgit
While GIT_SSH does not require any escaping / quoting (e.g. for paths
containing spaces), SVN_SSH requires it due to its use in a Perl script.
Previously, SVN_SSH has only been escaped and quoted automatically if it
was unset and thus derived from GIT_SSH. For user convenience, do the
escaping and quoting also for a SVN_SSH set by the user. This way, the
user is able to use the same unescaped and unquoted syntax for GIT_SSH
and SVN_SSH.
Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
---
git-svn.perl | 15 +++++++--------
1 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 89f83fd..c3b4b58 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -22,14 +22,13 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
$Git::SVN::Ra::_log_window_size = 100;
$Git::SVN::_minimize_url = 'unset';
-if (! exists $ENV{SVN_SSH}) {
- if (exists $ENV{GIT_SSH}) {
- $ENV{SVN_SSH} = $ENV{GIT_SSH};
- if ($^O eq 'msys') {
- $ENV{SVN_SSH} =~ s/\\/\\\\/g;
- $ENV{SVN_SSH} =~ s/(.*)/"$1"/;
- }
- }
+if (! exists $ENV{SVN_SSH} && exists $ENV{GIT_SSH}) {
+ $ENV{SVN_SSH} = $ENV{GIT_SSH};
+}
+
+if (exists $ENV{SVN_SSH} && $^O eq 'msys') {
+ $ENV{SVN_SSH} =~ s/\\/\\\\/g;
+ $ENV{SVN_SSH} =~ s/(.*)/"$1"/;
}
$Git::SVN::Log::TZ = $ENV{TZ};
--
1.7.6.GIT
^ permalink raw reply related
* Re: pack-object poor performance (with large number of objects?)
From: Piotr Krukowiecki @ 2011-10-05 8:48 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, Shawn Pearce, Git Mailing List, Ingo Molnar
In-Reply-To: <20111004180829.GB31671@sigill.intra.peff.net>
On Tue, Oct 4, 2011 at 8:08 PM, Jeff King <peff@peff.net> wrote:
> On Tue, Oct 04, 2011 at 03:21:24PM +0200, Piotr Krukowiecki wrote:
>
>> I have 4GB ram + 4GB swap. Is it possible the RAM is the problem if I
>> always have free RAM left and my swap is almost not used?
>> For example at the moment repack finished counting objects ("Counting
>> objects: 1742200, done."):
>>
>> $ free -m
>> total used free shared buffers cached
>> Mem: 3960 3814 146 0 441 215
>> -/+ buffers/cache: 3157 803
>> Swap: 6143 694 5449
>
[...]
> I have no idea if this will actually go faster for you. But it might be
> worth trying, instead of just redoing the svn import with auto-gc turned
> on.
I've left it to run over night and it finished (took almost 12 hours),
so hopefully I'm not going to run into this problem anymore.
$ time git repack -a -d -f
Counting objects: 1742200, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (1291909/1291909), done.
Writing objects: 100% (1742200/1742200), done.
Total 1742200 (delta 1094325), reused 39192 (delta 0)
Removing duplicate objects: 100% (256/256), done.
real 704m3.477s
user 65m35.960s
sys 9m50.880s
$ du -sh .git/objects/pack
3.9G .git/objects/pack
$ git count-objects -v
count: 0
size: 0
in-pack: 1742200
packs: 1
size-pack: 4078245
prune-packable: 0
garbage: 0
--
Piotr Krukowiecki
^ permalink raw reply
* Re: [PATCH WIP 0/3] git log --exclude
From: Nguyen Thai Ngoc Duy @ 2011-10-05 8:28 UTC (permalink / raw)
To: Matthieu Moy; +Cc: git, Jonathan Nieder
In-Reply-To: <vpqd3ebn9nc.fsf@bauges.imag.fr>
2011/10/5 Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>:
> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
>
>> This series adds --exclude that uses .gitignore mechanism remove
>> commits whose changes that are _entirely_ excluded.
>
> I'd see this --exclude as the opposite of specifying files, i.e. in a
> repository containing directories A, B and C,
>
> git log --exclude=B
>
> would be the same as
>
> git log A C
I think I emphasized it too much. "git log --exclude=B/ A B C" should
be equivalent to "git log A C". If changes touch A or C, then no
matter they touch B, the commit will always be in. If changes only
touch B, neither A nor B, then the commit is removed, exactly the same
case with "git log A C".
>> Because it uses .gitignore mechanism, beware that these patterns do
>> not behave exactly like pathspecs
if you specify --exclude=B, then A/.../B, C/.../B are all removed. A
subtle difference between pathspec and .gitignore.
> and because "git log --stat A C" (or --patch) will show the diff only
> for A and C for commits touching all directories.
I'll take care of --patch and friends later. They both (--patch and
commit pruning) use the same diff mechanism, if we get it right for
for commit pruning, --patch will come nicely.
--
Duy
^ permalink raw reply
* [PATCH] Report errors related to .git access during repository discovery
From: Nguyễn Thái Ngọc Duy @ 2011-10-05 8:17 UTC (permalink / raw)
To: Johannes Sixt
Cc: git, Federico Lucifredi, Nguyễn Thái Ngọc Duy
In-Reply-To: <4E8BF519.8090509@viscovery.net>
If $GIT_DIR is not given, we go up step by step and look for potential
repository directory, may see .git directory but for some reasons we
decide to skip and move on.
It's probably better to report along the line, so users can stop
wondering "hey, but I have .git directory _there_".
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
On Wed, Oct 5, 2011 at 5:11 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
> Am 10/4/2011 23:24, schrieb Federico Lucifredi:
>> Hello Git list,
>> Found a minor bug in git today - the error message reported is not
>> correct when trying to access a repo that is not accessible
>> permission-wise:
>>
>>> federico@skyplex:/etc$ git log
>>> fatal: Not a git repository (or any of the parent directories): .git
>>
>> with correct access permissions, everything works as expected.
>
> And the correct error message is...?
That's a correct message. But it'd be even better if we help diagnose
why. Even when you have proper access to .git dir, git can still
refuse to accept the directory as a repository, because "HEAD" is
invalid for example.
I think a patch like this is an improvement. There may be many
situations git refuses a directory but I don't cover here. Well, we
may when users report them
setup.c | 23 +++++++++++++++++++----
1 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/setup.c b/setup.c
index 27c1d47..b6028e5 100644
--- a/setup.c
+++ b/setup.c
@@ -269,6 +269,19 @@ const char *pathspec_prefix(const char *prefix, const char **pathspec)
}
/*
+ * This function is used during .git detection phase. If .git does not
+ * exist, it's OK not to report because that happens a lot if you stay
+ * inside a subdirectory and git checks every level back to topdir.
+ */
+static int access_and_warn(const char *path, int perm)
+{
+ int ret = access(path, perm);
+ if (ret && errno != ENOENT)
+ error("%s: %s", absolute_path(path), strerror(errno));
+ return ret;
+}
+
+/*
* Test if it looks like we're at a git directory.
* We want to see:
*
@@ -288,22 +301,24 @@ static int is_git_directory(const char *suspect)
die("Too long path: %.*s", 60, suspect);
strcpy(path, suspect);
if (getenv(DB_ENVIRONMENT)) {
- if (access(getenv(DB_ENVIRONMENT), X_OK))
+ if (access_and_warn(getenv(DB_ENVIRONMENT), X_OK))
return 0;
}
else {
strcpy(path + len, "/objects");
- if (access(path, X_OK))
+ if (access_and_warn(path, X_OK))
return 0;
}
strcpy(path + len, "/refs");
- if (access(path, X_OK))
+ if (access_and_warn(path, X_OK))
return 0;
strcpy(path + len, "/HEAD");
- if (validate_headref(path))
+ if (validate_headref(path)) {
+ error("invalid HEAD at %s", absolute_path(path));
return 0;
+ }
return 1;
}
--
1.7.3.1.256.g2539c.dirty
^ permalink raw reply related
* Re: [PATCH WIP 0/3] git log --exclude
From: Matthieu Moy @ 2011-10-05 8:08 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, Jonathan Nieder
In-Reply-To: <1317799088-26626-1-git-send-email-pclouds@gmail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> This series adds --exclude that uses .gitignore mechanism remove
> commits whose changes that are _entirely_ excluded.
I'd see this --exclude as the opposite of specifying files, i.e. in a
repository containing directories A, B and C,
git log --exclude=B
would be the same as
git log A C
If I understand correctly, it's not the case with your implementation
because
> Because it uses .gitignore mechanism, beware that these patterns do
> not behave exactly like pathspecs
and because "git log --stat A C" (or --patch) will show the diff only
for A and C for commits touching all directories.
--
Matthieu Moy
http://www-verimag.imag.fr/~moy/
^ permalink raw reply
* Re: Git Bug report
From: Fredrik Gustafsson @ 2011-10-05 7:22 UTC (permalink / raw)
To: Federico Lucifredi; +Cc: git
In-Reply-To: <1317763443.17036.15.camel@skyplex>
On Tue, Oct 04, 2011 at 05:24:03PM -0400, Federico Lucifredi wrote:
> Found a minor bug in git today - the error message reported is not
> correct when trying to access a repo that is not accessible
> permission-wise:
>
> > federico@skyplex:/etc$ git log
> > fatal: Not a git repository (or any of the parent directories): .git
>
> with correct access permissions, everything works as expected.
So if:
.git/ is a directory with not enought permissions.
../.git/ is a directory with enought permissions.
git would today use ../.git. You suggest that git instead would die
because a .git/ exists? (I'm not saying this is wrong or right).
--
Med vänliga hälsningar
Fredrik Gustafsson
E-post: iveqy@iveqy.com
Tel. nr.: 0733 60 82 74
^ permalink raw reply
* [PATCH WIP 3/3] log: add --exclude option
From: Nguyễn Thái Ngọc Duy @ 2011-10-05 7:18 UTC (permalink / raw)
To: git; +Cc: Jonathan Nieder, Nguyễn Thái Ngọc Duy
In-Reply-To: <1317799088-26626-1-git-send-email-pclouds@gmail.com>
This only helps filtering out commits whose changes are _entirely_ filtered out.
This does not affect patch generate (--patch, --stat and so on)
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
revision.c | 7 +++++--
revision.h | 2 ++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/revision.c b/revision.c
index c46cfaa..8472553 100644
--- a/revision.c
+++ b/revision.c
@@ -334,8 +334,8 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct
tree_difference = REV_TREE_SAME;
DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
- if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
- &revs->pruning) < 0)
+ if (diff_tree_sha1_with_exclude(t1->object.sha1, t2->object.sha1, "",
+ &revs->pruning, &revs->el, 0, 0) < 0)
return REV_TREE_DIFFERENT;
return tree_difference;
}
@@ -1454,6 +1454,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
return argcount;
} else if (!strcmp(arg, "--log-size")) {
revs->show_log_size = 1;
+ } else if (!prefixcmp(arg, "--exclude=")) {
+ add_exclude(arg + 10, "", 0, &revs->el);
+ revs->prune = 1;
}
/*
* Grepping the commit log
diff --git a/revision.h b/revision.h
index 3d64ada..3709959 100644
--- a/revision.h
+++ b/revision.h
@@ -4,6 +4,7 @@
#include "parse-options.h"
#include "grep.h"
#include "notes.h"
+#include "dir.h"
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
@@ -133,6 +134,7 @@ struct rev_info {
/* diff info for patches and for paths limiting */
struct diff_options diffopt;
struct diff_options pruning;
+ struct exclude_list el;
struct reflog_walk_info *reflog_info;
struct decoration children;
--
1.7.3.1.256.g2539c.dirty
^ permalink raw reply related
* [PATCH WIP 2/3] tree-diff: teach it to understand exclude patterns
From: Nguyễn Thái Ngọc Duy @ 2011-10-05 7:18 UTC (permalink / raw)
To: git; +Cc: Jonathan Nieder, Nguyễn Thái Ngọc Duy
In-Reply-To: <1317799088-26626-1-git-send-email-pclouds@gmail.com>
We introduce a new set of API, diff_tree_*_with_exclude, that also
exclude entries based on .gitignore patterns.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
diff.h | 11 +++++++++++
tree-diff.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/diff.h b/diff.h
index 8c66b59..51c8a5f 100644
--- a/diff.h
+++ b/diff.h
@@ -12,6 +12,7 @@ struct diff_queue_struct;
struct strbuf;
struct diff_filespec;
struct userdiff_driver;
+struct exclude_list;
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
@@ -170,8 +171,18 @@ extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
extern void diff_tree_release_paths(struct diff_options *);
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
const char *base, struct diff_options *opt);
+extern int diff_tree_with_exclude(struct tree_desc *t1, struct tree_desc *t2,
+ const char *base, struct diff_options *opt,
+ struct exclude_list *el,
+ int def_excl1, int def_excl2);
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
const char *base, struct diff_options *opt);
+extern int diff_tree_sha1_with_exclude(const unsigned char *old,
+ const unsigned char *new,
+ const char *base,
+ struct diff_options *opt,
+ struct exclude_list *el,
+ int def_excl1, int def_excl2);
extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
struct diff_options *opt);
diff --git a/tree-diff.c b/tree-diff.c
index b3cc2e4..9938ccf 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -5,12 +5,14 @@
#include "diff.h"
#include "diffcore.h"
#include "tree.h"
+#include "dir.h"
static void show_entry(struct diff_options *opt, const char *prefix,
struct tree_desc *desc, struct strbuf *base);
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
- struct strbuf *base, struct diff_options *opt)
+ struct strbuf *base, struct diff_options *opt,
+ struct exclude_list *el, int def_excl1, int def_excl2)
{
unsigned mode1, mode2;
const char *path1, *path2;
@@ -52,7 +54,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
sha1, sha2, base->buf, 0, 0);
}
strbuf_addch(base, '/');
- diff_tree_sha1(sha1, sha2, base->buf, opt);
+ diff_tree_sha1_with_exclude(sha1, sha2, base->buf, opt,
+ el, def_excl1, def_excl2);
} else {
opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
}
@@ -113,6 +116,31 @@ static void show_entry(struct diff_options *opt, const char *prefix,
strbuf_setlen(base, old_baselen);
}
+static int skip_excludes(struct tree_desc *t, struct strbuf *base,
+ struct exclude_list *el, int defval)
+{
+ for (; t->size; update_tree_entry(t)) {
+ /*
+ * excluded_from_list only cares whether dtype is
+ * DT_DIR or something else (except DT_UNKNOWN). Any
+ * other value would do
+ */
+ int dtype = S_ISDIR(t->entry.mode) ? DT_DIR : DT_REG;
+ int ret = excluded_from_list(base->buf, base->len, t->entry.path,
+ &dtype, el);
+
+ /* If undecided, use matching result of parent dir in defval */
+ if (ret < 0)
+ ret = defval;
+
+ if (ret == 1 && dtype == DT_REG)
+ ;
+ else
+ return ret;
+ }
+ return defval;
+}
+
static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
struct diff_options *opt, int *match)
{
@@ -130,9 +158,17 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
const char *base_str, struct diff_options *opt)
{
+ return diff_tree_with_exclude(t1, t2, base_str, opt, NULL, 0, 0);
+}
+
+int diff_tree_with_exclude(struct tree_desc *t1, struct tree_desc *t2,
+ const char *base_str, struct diff_options *opt,
+ struct exclude_list *el, int def_excl1, int def_excl2)
+{
struct strbuf base;
int baselen = strlen(base_str);
int t1_match = 0, t2_match = 0;
+ int excl1 = 0, excl2 = 0;
/* Enable recursion indefinitely */
opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
@@ -148,6 +184,10 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
skip_uninteresting(t1, &base, opt, &t1_match);
skip_uninteresting(t2, &base, opt, &t2_match);
}
+ if (el && el->nr) {
+ excl1 = skip_excludes(t1, &base, el, def_excl1);
+ excl2 = skip_excludes(t2, &base, el, def_excl2);
+ }
if (!t1->size) {
if (!t2->size)
break;
@@ -160,7 +200,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
update_tree_entry(t1);
continue;
}
- switch (compare_tree_entry(t1, t2, &base, opt)) {
+ switch (compare_tree_entry(t1, t2, &base, opt, el, excl1, excl2)) {
case -1:
update_tree_entry(t1);
continue;
@@ -267,6 +307,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
{
+ return diff_tree_sha1_with_exclude(old, new, base, opt, NULL, 0, 0);
+}
+
+int diff_tree_sha1_with_exclude(const unsigned char *old,
+ const unsigned char *new,
+ const char *base,
+ struct diff_options *opt,
+ struct exclude_list *el,
+ int def_excl1, int def_excl2)
+{
void *tree1, *tree2;
struct tree_desc t1, t2;
unsigned long size1, size2;
@@ -280,7 +330,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
die("unable to read destination tree (%s)", sha1_to_hex(new));
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
- retval = diff_tree(&t1, &t2, base, opt);
+ retval = diff_tree_with_exclude(&t1, &t2, base, opt, el, def_excl1, def_excl2);
if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
--
1.7.3.1.256.g2539c.dirty
^ permalink raw reply related
* [PATCH WIP 1/3] diff-no-index: rename read_directory to avoid conflict from dir.h
From: Nguyễn Thái Ngọc Duy @ 2011-10-05 7:18 UTC (permalink / raw)
To: git; +Cc: Jonathan Nieder, Nguyễn Thái Ngọc Duy
In-Reply-To: <1317799088-26626-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
diff-no-index.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/diff-no-index.c b/diff-no-index.c
index 3a36144..927abb7 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -16,7 +16,7 @@
#include "builtin.h"
#include "string-list.h"
-static int read_directory(const char *path, struct string_list *list)
+static int read_entire_directory(const char *path, struct string_list *list)
{
DIR *dir;
struct dirent *e;
@@ -68,9 +68,9 @@ static int queue_diff(struct diff_options *o,
struct string_list p2 = STRING_LIST_INIT_DUP;
int len1 = 0, len2 = 0, i1, i2, ret = 0;
- if (name1 && read_directory(name1, &p1))
+ if (name1 && read_entire_directory(name1, &p1))
return -1;
- if (name2 && read_directory(name2, &p2)) {
+ if (name2 && read_entire_directory(name2, &p2)) {
string_list_clear(&p1, 0);
return -1;
}
--
1.7.3.1.256.g2539c.dirty
^ permalink raw reply related
* [PATCH WIP 0/3] git log --exclude
From: Nguyễn Thái Ngọc Duy @ 2011-10-05 7:18 UTC (permalink / raw)
To: git; +Cc: Jonathan Nieder, Nguyễn Thái Ngọc Duy
This series adds --exclude that uses .gitignore mechanism remove
commits whose changes that are _entirely_ excluded. The main patch is
2/3 where it teaches diff_tree_* about struct exclude_list.
Because it uses .gitignore mechanism, beware that these patterns do
not behave exactly like pathspecs (patterns without slashes match
every directory, for example)
I tried these commands
time git log --stat >/dev/null
time git log --stat --exclude=Documentation >/dev/null
The former took 37 secs, the latter 40 secs. Not bad, but there is
definitely room for improvement. skip_excludes() should be able to
point out whether an entire directory is excluded and skip the whole
directory (as opposed to descending in and checking files one by one
now, in fear of negative patterns). These kinds of optimizations
benefit sparse checkout too.
I think I made a mistake somewhere because the above command seems to
remove more commits than it should... Regardless,
"git log --exclude=po" on gnome-shell looks sooo clean.
Nguyễn Thái Ngọc Duy (3):
diff-no-index: rename read_directory to avoid conflict from dir.h
tree-diff: teach it to understand exclude patterns
log: add --exclude option
diff-no-index.c | 6 ++--
diff.h | 11 ++++++++++
revision.c | 7 ++++-
revision.h | 2 +
tree-diff.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 75 insertions(+), 9 deletions(-)
--
1.7.3.1.256.g2539c.dirty
^ permalink raw reply
* Re: What's cooking in git.git (Oct 2011, #01; Tue, 4)
From: Johannes Sixt @ 2011-10-05 6:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vvcs49ofl.fsf@alter.siamese.dyndns.org>
Am 10/5/2011 4:12, schrieb Junio C Hamano:
> * il/archive-err-signal (2011-10-03) 1 commit
> - transport: do not allow to push over git:// protocol
Kindly fix up the commit message (delete the cruft) before you merge to next.
Thanks,
-- Hannes
^ permalink raw reply
* Re: [PATCH] use -h for synopsis and --help for manpage consistently
From: Clemens Buchacher @ 2011-10-05 6:33 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vhb3penqw.fsf@alter.siamese.dyndns.org>
On Mon, Oct 03, 2011 at 03:03:51PM -0700, Junio C Hamano wrote:
> >
> > -my @opts = ( 'help|h|H', 'version|V',
> > +my @opts = ( 'h', 'version|V',
>
> I am a bit skeptical about the removal of 'H' here (also in git-svn).
I see. I'm ok with your fixup.
Clemens
^ permalink raw reply
* [PATCH] fix push --quiet: add 'quiet' capability to receive-pack
From: Clemens Buchacher @ 2011-10-05 6:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vaa9xyxpf.fsf@alter.siamese.dyndns.org>
Hi Junio,
Since 1.7.7 is out now, maybe you would like to pick this up again.
Cheers,
Clemens
On Wed, Sep 21, 2011 at 10:04:28PM -0700, Junio C Hamano wrote:
>
> * cb/maint-quiet-push (2011-09-05) 4 commits
> . t5541: avoid TAP test miscounting
> . push: old receive-pack does not understand --quiet
> . fix push --quiet via http
> . tests for push --quiet
>
> Dropped for rerolling after 1.7.7 cycle.
This is the re-rolled version based on current master, without the
backwards incompatible receive-pack --quiet option.
I squashed the tests in and added your unpack(void) fixup.
I have not added Michael's "t5541: avoid TAP test miscounting"
since I cannot reproduce the error:
http://mid.gmane.org/e4e82f1267da3edfc600361de0041f618c31e30c.1315232475.git.git@drmicha.warpmail.net
And there is also this related patch "server_supports(): parse
feature list more carefully", which looked good to me:
http://mid.gmane.org/7vmxejy9od.fsf@alter.siamese.dyndns.org
Does it need more work?
Clemens
--o<--
Currently, git push --quiet produces some non-error output, e.g.:
$ git push --quiet
Unpacking objects: 100% (3/3), done.
This fixes a bug reported for the fedora git package:
https://bugzilla.redhat.com/show_bug.cgi?id=725593
Commit 90a6c7d4 (propagate --quiet to send-pack/receive-pack)
introduced the --quiet option to receive-pack and made send-pack
pass that option. Older versions of receive-pack do not recognize
the option, however, and terminate immediately. The commit was
therefore reverted.
This change instead adds a 'quiet' capability to receive-pack,
which is a backwards compatible.
In addition, this fixes push --quiet via http: A verbosity of 0
means quiet for remote helpers.
Reported-by: Jesse Keating <jkeating@redhat.com>
Reported-by: Tobias Ulmer <tobiasu@tmux.org>
Cc: Todd Zullinger <tmz@pobox.com>
Signed-off-by: Clemens Buchacher <drizzd@aon.at>
---
builtin/receive-pack.c | 14 ++++++++++++--
builtin/send-pack.c | 13 ++++++++++---
remote-curl.c | 4 +++-
t/t5523-push-upstream.sh | 7 +++++++
t/t5541-http-push.sh | 8 ++++++++
5 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ae164da..4419323 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -31,6 +31,7 @@ static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
+static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
@@ -114,7 +115,7 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
else
packet_write(1, "%s %s%c%s%s\n",
sha1_to_hex(sha1), path, 0,
- " report-status delete-refs side-band-64k",
+ " report-status delete-refs side-band-64k quiet",
prefer_ofs_delta ? " ofs-delta" : "");
sent_capabilities = 1;
return 0;
@@ -636,6 +637,8 @@ static struct command *read_head_info(void)
report_status = 1;
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
+ if (strstr(refname + reflen + 1, "quiet"))
+ quiet = 1;
}
cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
@@ -684,8 +687,10 @@ static const char *unpack(void)
if (ntohl(hdr.hdr_entries) < unpack_limit) {
int code, i = 0;
- const char *unpacker[4];
+ const char *unpacker[5];
unpacker[i++] = "unpack-objects";
+ if (quiet)
+ unpacker[i++] = "-q";
if (receive_fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
@@ -799,6 +804,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
const char *arg = *argv++;
if (*arg == '-') {
+ if (!strcmp(arg, "--quiet")) {
+ quiet = 1;
+ continue;
+ }
+
if (!strcmp(arg, "--advertise-refs")) {
advertise_refs = 1;
continue;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index c1f6ddd..a8d6b4c 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -263,6 +263,8 @@ int send_pack(struct send_pack_args *args,
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
use_sideband = 1;
+ if (!server_supports("quiet"))
+ args->quiet = 0;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -301,11 +303,12 @@ int send_pack(struct send_pack_args *args,
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
- if (!cmds_sent && (status_report || use_sideband)) {
- packet_buf_write(&req_buf, "%s %s %s%c%s%s",
+ if (!cmds_sent && (status_report || use_sideband || args->quiet)) {
+ packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
old_hex, new_hex, ref->name, 0,
status_report ? " report-status" : "",
- use_sideband ? " side-band-64k" : "");
+ use_sideband ? " side-band-64k" : "",
+ args->quiet ? " quiet" : "");
}
else
packet_buf_write(&req_buf, "%s %s %s",
@@ -439,6 +442,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.force_update = 1;
continue;
}
+ if (!strcmp(arg, "--quiet")) {
+ args.quiet = 1;
+ continue;
+ }
if (!strcmp(arg, "--verbose")) {
args.verbose = 1;
continue;
diff --git a/remote-curl.c b/remote-curl.c
index b8cf45a..2341106 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -762,7 +762,9 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
argv[argc++] = "--thin";
if (options.dry_run)
argv[argc++] = "--dry-run";
- if (options.verbosity > 1)
+ if (options.verbosity == 0)
+ argv[argc++] = "--quiet";
+ else if (options.verbosity > 1)
argv[argc++] = "--verbose";
argv[argc++] = url;
for (i = 0; i < nr_spec; i++)
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index c229fe6..9ee52cf 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -108,4 +108,11 @@ test_expect_failure TTY 'push --no-progress suppresses progress' '
! grep "Writing objects" err
'
+test_expect_success TTY 'quiet push' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
+ test_cmp /dev/null output
+'
+
test_done
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
index a73c826..e756a08 100755
--- a/t/t5541-http-push.sh
+++ b/t/t5541-http-push.sh
@@ -5,6 +5,7 @@
test_description='test smart pushing over http via http-backend'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
if test -n "$NO_CURL"; then
skip_all='skipping test, git built without http support'
@@ -154,5 +155,12 @@ test_expect_success 'push (chunked)' '
test $HEAD = $(git rev-parse --verify HEAD))
'
+test_expect_success TTY 'quiet push' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit quiet &&
+ test_terminal git push --quiet --no-progress 2>&1 | tee output &&
+ test_cmp /dev/null output
+'
+
stop_httpd
test_done
--
1.7.6.1
^ permalink raw reply related
* Re: Git Bug report
From: Johannes Sixt @ 2011-10-05 6:11 UTC (permalink / raw)
To: Federico Lucifredi; +Cc: git
In-Reply-To: <1317763443.17036.15.camel@skyplex>
Am 10/4/2011 23:24, schrieb Federico Lucifredi:
> Hello Git list,
> Found a minor bug in git today - the error message reported is not
> correct when trying to access a repo that is not accessible
> permission-wise:
>
>> federico@skyplex:/etc$ git log
>> fatal: Not a git repository (or any of the parent directories): .git
>
> with correct access permissions, everything works as expected.
And the correct error message is...?
>> drwx------ 8 root root 4096 2011-10-03 16:53 .git
Assuming that you expected something like this:
fatal: .git: permission denied
it is hard to argue that a directory that happens to be named .git, but
was sealed by its owner should be assumed to be a git repository, albeit
one that we do not have access to. "Not a git repository" is an equally
justifyable error message, IMHO.
-- Hannes
^ permalink raw reply
* Re: [PATCH 0/9] i18n: add PO files to po/
From: Ramkumar Ramachandra @ 2011-10-05 4:09 UTC (permalink / raw)
To: Junio C Hamano
Cc: Ævar Arnfjörð, Jonathan Nieder, git,
Peter Krefting, Marcin Cieślak, Sam Reed, Jan Engelhardt,
Jan Krüger, Nguyễn Thái Ngọc
In-Reply-To: <7vaa9gbdyc.fsf@alter.siamese.dyndns.org>
Hi,
Junio C Hamano writes:
> [...]
> have been hinting me to eject everything under the current po/ directory,
> and bind that part of the tree as a submodule from another repository,
> [...]
Desirable side effect: more people will start working on improving
git-submodule :)
-- Ram
^ permalink raw reply
* Re: [RFC/PATCH] Add multiple workdir support to branch/checkout
From: Junio C Hamano @ 2011-10-05 4:07 UTC (permalink / raw)
To: Jay Soffian; +Cc: git
In-Reply-To: <1317786204-57335-1-git-send-email-jaysoffian@gmail.com>
Jay Soffian <jaysoffian@gmail.com> writes:
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 5e356a6c61..26259a41a7 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -709,12 +710,35 @@ static void orphaned_commit_warning(struct commit *commit)
> for_each_ref(clear_commit_marks_from_one_ref, NULL);
> }
>
> +static void record_checkout(const char *name, const char *work_tree)
> +{
> + struct strbuf key = STRBUF_INIT;
> + strbuf_addf(&key, "branch.%s.checkout", name);
> + git_config_set(key.buf, work_tree);
> + strbuf_release(&key);
> +}
> +
> +static void check_if_checked_out(struct checkout_opts *opts, const char *name)
> +{
> + struct branch *branch = branch_get(name);
> + if (branch->work_tree && strlen(branch->work_tree) &&
> + strcmp(branch->work_tree, get_git_work_tree())) {
> + if (opts->force)
> + warning(_("branch '%s' is currently checked out"
> + " in '%s'"), name, branch->work_tree);
> + else
> + die(_("branch '%s' is currently checked out"
> + " in '%s'"), name, branch->work_tree);
> + }
> +}
> +
> static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
> {
> int ret = 0;
> struct branch_info old;
> unsigned char rev[20];
> int flag;
> +
> memset(&old, 0, sizeof(old));
> old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
> old.commit = lookup_commit_reference_gently(rev, 1);
> @@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
> parse_commit(new->commit);
> }
>
> + if (opts->record_checkouts)
> + check_if_checked_out(opts, new->name);
The close brace we can see in the context closes "if (!new->name) {", so
this codepath is very well prepared to be called with new->name == NULL.
Is check_if_checked_out() prepared to be called with name == NULL and do
the right thing?
> @@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
>
> update_refs_for_switch(opts, &old, new);
>
> + if (opts->record_checkouts) {
> + const char *work_tree = get_git_work_tree();
> + struct branch *branch = branch_get(old.name);
> + if (branch->work_tree && !strcmp(branch->work_tree, work_tree))
> + record_checkout(old.name, "");
> + record_checkout(new->name, work_tree);
> + }
> +
Likewise for new->name, but also old.name which is only set when old.path
is set and begins with "refs/heads/" and otherwise NULL.
^ permalink raw reply
* Re: [RFC/PATCH] Add multiple workdir support to branch/checkout
From: Nguyen Thai Ngoc Duy @ 2011-10-05 4:02 UTC (permalink / raw)
To: Jay Soffian; +Cc: git
In-Reply-To: <1317786204-57335-1-git-send-email-jaysoffian@gmail.com>
On Wed, Oct 5, 2011 at 2:43 PM, Jay Soffian <jaysoffian@gmail.com> wrote:
> When using 'git new-workdir', there is no safety mechanism to prevent the
> same branch from being checked out twice, nor to prevent a checked out
> branch from being deleted.
>
> By teaching 'checkout' to record the workdir path using
> 'branch.<name>.checkout' when switching branches, we can easily check if a
> branch is already checked out in another workdir before switching to that
> branch. Similarly, we can now add a check before deleting a branch.
>
> Allow 'checkout -f' to force the checkout and issue a warning
> instead of an error.
>
> Guard this behavior behind 'core.recordCheckouts', which we will
> teach 'git new-workdir' to set in a followup commit.
I've wanted to to something like this, but you beat me to it ;)
Could you please consider a more generic approach? What I have in mind
is a mechanism to "lock" a branch, so that only commands that have the
key can update it.
So instead of branch.<name>.checkout, I would have something like
branch.<name>.locked = <key>, where <key> is just a string. Only
commands that provide the matching <key> are allowed to update the
branch. In checkout case, <key> could be "checkout: worktree".
This approach addresses more cases than just multiple workdir. We
could relax restrictions on pushing to a non-bare repository: we only
disallow pushing to locked branches. We can also use this to prevent
users from checking out another branch (by locking HEAD) while in the
middle of interactive rebase/bisect/...
--
Duy
^ permalink raw reply
* Re: [RFC/PATCH] Add multiple workdir support to branch/checkout
From: Jay Soffian @ 2011-10-05 3:48 UTC (permalink / raw)
To: git; +Cc: Jay Soffian
In-Reply-To: <1317786204-57335-1-git-send-email-jaysoffian@gmail.com>
On Tue, Oct 4, 2011 at 11:43 PM, Jay Soffian <jaysoffian@gmail.com> wrote:
> When using 'git new-workdir', there is no safety mechanism to prevent the
> same branch from being checked out twice, nor to prevent a checked out
> branch from being deleted.
>
> By teaching 'checkout' to record the workdir path using
> 'branch.<name>.checkout' when switching branches, we can easily check if a
> branch is already checked out in another workdir before switching to that
> branch. Similarly, we can now add a check before deleting a branch.
>
> Allow 'checkout -f' to force the checkout and issue a warning
> instead of an error.
>
> Guard this behavior behind 'core.recordCheckouts', which we will
> teach 'git new-workdir' to set in a followup commit.
Well, depending upon what folks think of this RFC, anyway.
> Note: when switching away from a branch, we set 'branch.<name>.checkout'
> to the empty string, instead of deleting it entirely, since git_config()
> otherwise leaves behind an empty section which it does not re-use.
Maybe this is a bug in git_config()? It seems like if it's removed the
last item from a section, it should remove the whole section OR it
should re-use an empty section.
j.
^ permalink raw reply
* [RFC/PATCH] Add multiple workdir support to branch/checkout
From: Jay Soffian @ 2011-10-05 3:43 UTC (permalink / raw)
To: git; +Cc: Jay Soffian
When using 'git new-workdir', there is no safety mechanism to prevent the
same branch from being checked out twice, nor to prevent a checked out
branch from being deleted.
By teaching 'checkout' to record the workdir path using
'branch.<name>.checkout' when switching branches, we can easily check if a
branch is already checked out in another workdir before switching to that
branch. Similarly, we can now add a check before deleting a branch.
Allow 'checkout -f' to force the checkout and issue a warning
instead of an error.
Guard this behavior behind 'core.recordCheckouts', which we will
teach 'git new-workdir' to set in a followup commit.
Note: when switching away from a branch, we set 'branch.<name>.checkout'
to the empty string, instead of deleting it entirely, since git_config()
otherwise leaves behind an empty section which it does not re-use.
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
builtin/branch.c | 10 ++++++++++
builtin/checkout.c | 39 +++++++++++++++++++++++++++++++++++++++
remote.c | 4 ++++
remote.h | 1 +
4 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index f49596f826..6ce1a5b133 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -182,6 +182,16 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
ret = 1;
continue;
}
+ if (kinds == REF_LOCAL_BRANCH) {
+ struct branch *branch = branch_get(bname.buf);
+ if (branch->work_tree && strlen(branch->work_tree)) {
+ error(_("Cannot delete the branch '%s' "
+ "which is currently checked out in '%s'"),
+ bname.buf, branch->work_tree);
+ ret = 1;
+ continue;
+ }
+ }
free(name);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5e356a6c61..26259a41a7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,7 @@ struct checkout_opts {
int force_detach;
int writeout_stage;
int writeout_error;
+ int record_checkouts;
/* not set by parse_options */
int branch_exists;
@@ -709,12 +710,35 @@ static void orphaned_commit_warning(struct commit *commit)
for_each_ref(clear_commit_marks_from_one_ref, NULL);
}
+static void record_checkout(const char *name, const char *work_tree)
+{
+ struct strbuf key = STRBUF_INIT;
+ strbuf_addf(&key, "branch.%s.checkout", name);
+ git_config_set(key.buf, work_tree);
+ strbuf_release(&key);
+}
+
+static void check_if_checked_out(struct checkout_opts *opts, const char *name)
+{
+ struct branch *branch = branch_get(name);
+ if (branch->work_tree && strlen(branch->work_tree) &&
+ strcmp(branch->work_tree, get_git_work_tree())) {
+ if (opts->force)
+ warning(_("branch '%s' is currently checked out"
+ " in '%s'"), name, branch->work_tree);
+ else
+ die(_("branch '%s' is currently checked out"
+ " in '%s'"), name, branch->work_tree);
+ }
+}
+
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
{
int ret = 0;
struct branch_info old;
unsigned char rev[20];
int flag;
+
memset(&old, 0, sizeof(old));
old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
old.commit = lookup_commit_reference_gently(rev, 1);
@@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
parse_commit(new->commit);
}
+ if (opts->record_checkouts)
+ check_if_checked_out(opts, new->name);
+
ret = merge_working_tree(opts, &old, new);
if (ret)
return ret;
@@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
update_refs_for_switch(opts, &old, new);
+ if (opts->record_checkouts) {
+ const char *work_tree = get_git_work_tree();
+ struct branch *branch = branch_get(old.name);
+ if (branch->work_tree && !strcmp(branch->work_tree, work_tree))
+ record_checkout(old.name, "");
+ record_checkout(new->name, work_tree);
+ }
+
ret = post_checkout_hook(old.commit, new->commit, 1);
free((char *)old.path);
return ret || opts->writeout_error;
@@ -756,6 +791,10 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "core.recordcheckouts")) {
+ struct checkout_opts *opts = cb;
+ opts->record_checkouts = git_config_bool(var, value);
+ }
if (!prefixcmp(var, "submodule."))
return parse_submodule_config_option(var, value);
diff --git a/remote.c b/remote.c
index b8ecfa5d95..2bc063dae8 100644
--- a/remote.c
+++ b/remote.c
@@ -364,6 +364,10 @@ static int handle_config(const char *key, const char *value, void *cb)
if (!value)
return config_error_nonbool(key);
add_merge(branch, xstrdup(value));
+ } else if (!strcmp(subkey, ".checkout")) {
+ if (!value)
+ return config_error_nonbool(key);
+ branch->work_tree = xstrdup(value);
}
return 0;
}
diff --git a/remote.h b/remote.h
index 9a30a9dba6..4103ec7e31 100644
--- a/remote.h
+++ b/remote.h
@@ -126,6 +126,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec);
struct branch {
const char *name;
const char *refname;
+ const char *work_tree;
const char *remote_name;
struct remote *remote;
--
1.7.7.4.g39e02c
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox