* Re: Build bug report: 'make check' needs sparse, but configure doesn't check it
From: Shawn O. Pearce @ 2008-10-06 6:58 UTC (permalink / raw)
To: Ed Avis; +Cc: git
In-Reply-To: <loom.20081005T094301-345@post.gmane.org>
Ed Avis <eda@waniasset.com> wrote:
> Dmitry Potapov <dpotapov <at> gmail.com> writes:
>
> >The whole point of 'make check' is to run 'sparce' on all files.
>
> >If you want to build
> >and to test the resulting binaries then you run 'make test'.
>
> To reduce confusion I suggest renaming the target to 'make sparse'. Then it's
> obvious what it does and it can't get confused with the more common usage of
> 'make check'.
Nah. We're not going to rename the target to "make sparse".
First off that sounds like we are compiling the sparse binary for
the user, and we will never do that. And existing developers who
use "make check" to run sparse are used to that calling convention.
--
Shawn.
^ permalink raw reply
* Re: [PATCH] error out if path is invalid
From: Johannes Sixt @ 2008-10-06 7:02 UTC (permalink / raw)
To: Dmitry Potapov; +Cc: Shawn O. Pearce, git
In-Reply-To: <1223172881-4948-2-git-send-email-dpotapov@gmail.com>
Dmitry Potapov schrieb:
> if (!verify_path(path))
> - return -1;
> + return error("Invalid path '%s'", path);
Look at this change. Didn't the code error out before, too? Same in the
other cases. Hence, your patch subject does not describe the patch. And
I'd appreciate if you could at least show an example in the description
what the patch fixes.
-- Hannes
^ permalink raw reply
* Re: [PATCH 2/2] check-attr: Add --stdin-paths option
From: Johannes Sixt @ 2008-10-06 7:09 UTC (permalink / raw)
To: Dmitry Potapov; +Cc: Alexander Gavrilov, git, Shawn O. Pearce, Paul Mackerras
In-Reply-To: <1223173855-6173-2-git-send-email-dpotapov@gmail.com>
Dmitry Potapov schrieb:
> +static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
> + const char** name)
> +{
> + struct strbuf buf, nbuf;
> +
> + strbuf_init(&buf, 0);
> + strbuf_init(&nbuf, 0);
> + while (strbuf_getline(&buf, stdin, '\n') != EOF) {
> + if (buf.buf[0] == '"') {
> + strbuf_reset(&nbuf);
> + if (unquote_c_style(&nbuf, buf.buf, NULL))
> + die("line is badly quoted");
> + strbuf_swap(&buf, &nbuf);
> + }
> + check_attr(cnt, check, name, buf.buf);
> + }
> + strbuf_release(&buf);
> + strbuf_release(&nbuf);
> +}
> +
We know that you will want to use this feature in gitk to reduce the
number of fork()s. But you've a problem: gitk will first write a path to
git-check-addr's stdin, and then wait for the result on its stdout. But
this is a classic pitfall: You are not guaranteed that something will be
returned from stdout right away due to buffering. The least that is needed
is fflush(stdout) in this loop (after each iteration!) so that gitk sees
some result and does not hang forever.
-- Hannes
^ permalink raw reply
* Re: Git and tagging hook
From: Andreas Ericsson @ 2008-10-06 7:17 UTC (permalink / raw)
To: Kristis Makris; +Cc: git, scmbug-users
In-Reply-To: <1223268332.4072.7.camel@localhost>
Kristis Makris wrote:
> Hello,
>
> It seems that Git (at least v1.5.6) does not offer hooks on tag creation
> (a pre-tag and a post-tag hook). I need such a hook for integrating tag
> activities with an issue-tracker. Is it possible to add this hook ?
>
What you want is probably the post-receive or 'update' hooks on whatever
repository you consider your public watering hole for your project.
Integrating with an issue-tracker from the developers machine would be
utterly stupid, as it would prevent tagging from happening while not
connected.
--
Andreas Ericsson andreas.ericsson@op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
^ permalink raw reply
* Re: Git and tagging hook
From: Andreas Ericsson @ 2008-10-06 7:20 UTC (permalink / raw)
To: Kristis Makris; +Cc: git
In-Reply-To: <1223268332.4072.7.camel@localhost>
Next time, don't include a members-only list in the CC, please. The
usual action when replying to an email on git@vger is to "Reply All",
which doesn't play nicely with such lists and will render one list
archive incomplete (since people will inevitably cull that other
list from their CC's after having sent to it once).
--
Andreas Ericsson andreas.ericsson@op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
^ permalink raw reply
* Re: [EGIT PATCH 2/6] Peel annotated tags when getting all refs
From: Shawn O. Pearce @ 2008-10-06 7:43 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1223249802-9959-3-git-send-email-robin.rosenberg@dewire.com>
FWIW I'm still going through the series. But this jumped out at me.
Robin Rosenberg <robin.rosenberg@dewire.com> wrote:
> For packed refs we got this automatically from packed-refs,
> but for loose tags we have to follow the tags and get the leaf
> object in order to comply with the documentation.
...
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
> index 5a1b85f..1ee70d9 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
> @@ -271,7 +271,16 @@ private void readOneLooseRef(final Map<String, Ref> avail,
> return;
> }
>
> - ref = new Ref(Ref.Storage.LOOSE, origName, refName, id);
> + Object tt = db.mapObject(id, refName);
> + if (tt != null && tt instanceof Tag) {
> + Tag t = (Tag)tt;
Owwww.
I don't want to be doing peeling of loose annotated tags anytime
we loop through the loose objects. That is just painful and very
expensive. I'd rather the peeling be caused on demand by callers
who need it, but if its done through a method on Repository then
we can push the peeled ObjectId back into the RefDatabase cache.
I'm thinking more like:
Repository db = ...;
...
ObjectId p = ref.getPeeledObjectId()
if (p == null)
p = db.peel(ref)
--
Shawn.
^ permalink raw reply
* Re: [EGIT PATCH 4/6] Add tags to the graphical history display.
From: Shawn O. Pearce @ 2008-10-06 8:08 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1223249802-9959-5-git-send-email-robin.rosenberg@dewire.com>
FYI, my comments don't fully cover this patch yet.
Robin Rosenberg <robin.rosenberg@dewire.com> wrote:
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/revplot/PlotCommit.java b/org.spearce.jgit/src/org/spearce/jgit/revplot/PlotCommit.java
> index 5a5ef1e..fac89f5 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/revplot/PlotCommit.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/revplot/PlotCommit.java
> @@ -58,14 +59,19 @@
>
> PlotCommit[] children;
>
> + Ref[] refs;
> +
> /**
> * Create a new commit.
> *
> * @param id
> * the identity of this commit.
> + * @param tags
> + * the tags associated with this commit, null for no tags
> */
> - protected PlotCommit(final AnyObjectId id) {
> + protected PlotCommit(final AnyObjectId id, final Ref[] tags) {
> super(id);
> + this.refs = tags;
this.refs isn't final. There is no reason to be adding it to the
constructor and having this ripple effect all the way up into the
RevWalk class.
Maybe PlotWalk should override next() and only set PlotCommit.refs on
things returned from super.next()? This way we only do tag lookup
on commits that the filtering rules have said should be shown in
to the application, but the refs should still be inserted prior to
the application seeing the RevCommit.
> @@ -54,6 +66,7 @@
> public PlotWalk(final Repository repo) {
> super(repo);
> super.sort(RevSort.TOPO, true);
> + reverseRefMap = repo.getAllRefsByPeeledObjectId();
I wonder if this shouldn't be done as part of the StartGenerator
(or something like it but specialized for PlotWalk). I only say
that because a reused PlotWalk may want to make sure its ref map
is current with the repository its about to walk against.
> + @Override
> + protected Ref[] getTags(final AnyObjectId commitId) {
> + List<Ref> list = reverseRefMap.get(commitId);
> + Ref[] tags;
> + if (list == null)
> + tags = null;
> + else {
> + if (list != null && list.size() > 1) {
> + Collections.sort(list, new Comparator<Ref>() {
> + public int compare(Ref o1, Ref o2) {
> + try {
> + Object obj1 = getRepository().mapObject(o1.getObjectId(), o1.getName());
> + Object obj2 = getRepository().mapObject(o2.getObjectId(), o2.getName());
> + long t1 = timeof(obj1);
> + long t2 = timeof(obj2);
> + if (t1 > t2)
> + return -1;
> + if (t1 < t2)
> + return 1;
> + return 0;
> + } catch (IOException e) {
> + // ignore
> + return 0;
> + }
> + }
> + long timeof(Object o) {
> + if (o instanceof Commit)
> + return ((Commit)o).getCommitter().getWhen().getTime();
> + if (o instanceof Tag)
> + return ((Tag)o).getTagger().getWhen().getTime();
> + return 0;
You are inside of a PlotWalk, which extends RevWalk, which has very
aggressive caching of RevCommit and RevTag data instances, and very
fast parsers. Much faster than the legacy Commit and Tag classes.
I think we should use the RevCommit and RevTag classes to handle
sorting here. RevCommit already has the committer time cached
and stored in an int. RevCommit probably should learn to do the
same for its "tagger" field. The tiny extra bloat (1 word) that
adds to a RevTag instance is worth it when we consider implementing
something like this and/or git-describe where sorting tags by their
dates is useful.
> + tags = list.toArray(new Ref[list.size()]);
I wonder if using a Ref[] here even makes sense given that the data
is stored in a List<Ref>. I use RevCommit[] inside of RevCommit
generally because the number of entries in the array is 1 or 2 and
the array is smaller than say an ArrayList.
In hindsight those RevCommit[] probably should be a List<RevCommit>
with different list implementations based on the number of parents
needed:
0 parents: Collections.emptyList()
1 parent: Collections.singletonList()
2 parents: some especialized AbstractList subclass
3 parents: ArrayList
I think it would actually use less memory per instance, which is
a huge bonus, but we'd pay a downcasting penalty on each access.
HotSpot is supposed to be really good at removing the downcast
penalty from say java.util.List, but I don't if it can beat a typed
array access.
Sorry I got off on a bit of a tangent here. I'm just trying to
point out that the primary reason I've usd an array before is
probably moot here since the data is already in a List.
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java b/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
> index d7e4c58..41d57c6 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/revwalk/RevWalk.java
> @@ -53,6 +53,7 @@
IMHO this class shouldn't need to be modified.
> @@ -541,7 +542,7 @@ public RevTree lookupTree(final AnyObjectId id) {
> public RevCommit lookupCommit(final AnyObjectId id) {
> RevCommit c = (RevCommit) objects.get(id);
> if (c == null) {
> - c = createCommit(id);
> + c = createCommit(id, getTags(id));
This code is performance critical to commit parsing. Trying to
lookup tags for every commit we evaluate, especially ones that we
will never show in the UI (because they are uninteresting) but that
we still need to parse in order to derive the merge base is just
a huge waste of time.
Same applies for the lookupAny and parseAny methods.
--
Shawn.
^ permalink raw reply
* Re: [EGIT PATCH 3/6] Add a method to get refs by object Id
From: Shawn O. Pearce @ 2008-10-06 8:15 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1223249802-9959-4-git-send-email-robin.rosenberg@dewire.com>
Robin Rosenberg <robin.rosenberg@dewire.com> wrote:
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
> index dfce1b8..3fc5236 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
> @@ -939,6 +940,33 @@ public String getBranch() throws IOException {
> }
>
> /**
> + * @return a map with all objects referenced by a peeled ref.
> + */
> + public Map<AnyObjectId, List<Ref>> getAllRefsByPeeledObjectId() {
Do we really want to promise List here? Can we make it just
Collection instead?
> + Map<String, Ref> allRefs = getAllRefs();
> + HashMap<AnyObjectId, List<Ref>> ret = new HashMap<AnyObjectId, List<Ref>>(allRefs.size());
> + for (Map.Entry<String,Ref> e : allRefs.entrySet()) {
> + Ref ref = e.getValue();
I think this is cleaner:
for (Ref ref : allRefs.values()) {
as you never use the key.
> + AnyObjectId target = ref.getPeeledObjectId();
> + if (target == null)
> + target = ref.getObjectId();
> + List<Ref> list = ret.get(target);
> + if (list == null) {
> + list = Collections.singletonList(ref);
> + } else {
> + if (list.size() == 1) {
> + ArrayList<Ref> list2 = new ArrayList<Ref>(2);
> + list2.add(list.get(0));
> + list = list2;
> + }
> + list.add(ref);
> + }
> + ret.put(target, list);
Hmm. Putting the list every time is pointless. This is may run
faster because we (on average) only do one hash lookup per target,
not 2:
List<Ref> nl = Collections.singletonList(ref);
List<Ref> ol = ret.put(target, nl);
if (ol != null) {
if (ol.size() == 1) {
nl = new ArrayList<Ref>(2);
nl.add(ol.get(0));
nl.add(ref);
ret.put(target, nl);
} else {
ol.add(ref)
ret.put(target, ol);
}
}
The trick is that most targets have exactly one Ref, so the first
ret.put will return null and we'll never worry about the expansion
cases. In the rare cases where more than one Ref points at the
same object we'll degrade into doing two hash lookups per target,
which is no worse than what you have right now.
--
Shawn.
^ permalink raw reply
* Re: [PATCH 0/4] diff text conversion filter
From: Johannes Sixt @ 2008-10-06 8:55 UTC (permalink / raw)
To: Jeff King; +Cc: Matthieu Moy, git
In-Reply-To: <20081006065212.GA19175@sigill.intra.peff.net>
Jeff King schrieb:
> On Mon, Oct 06, 2008 at 08:29:10AM +0200, Johannes Sixt wrote:
>
>> Does the series in any way change whether plumbing and porcelain invoke
>> the external diff drivers? I have this particular use-case, which I'd like
>> that still works:
>
> No, it tries to keep the behavior the same as it is now (in 2/4, note
> how the diff driver config reading is split into porcelain and plumbing
> sections). Let me know if you have a test case that doesn't work, or
> that you would like me to try.
A quick check shows that my use-case still works as expected. Thanks.
-- Hannes
^ permalink raw reply
* [QGIT PATCH] Rework the commit confirmation box a bit
From: Abdelrazak Younes @ 2008-10-06 9:11 UTC (permalink / raw)
To: Git Mailing List
The problem was that the dialog was too big for my whenever too many files
were changed. Now, the list of changed files is only shown whenever they are
less than 20; otherwise it is shown in the detailed text accessible though
the 'Show Detail' button.
---
src/commitimpl.cpp | 25 ++++++++++++++++++++-----
1 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/src/commitimpl.cpp b/src/commitimpl.cpp
index 1540947..def5209 100644
--- a/src/commitimpl.cpp
+++ b/src/commitimpl.cpp
@@ -236,17 +236,32 @@ bool CommitImpl::checkConfirm(SCRef msg, SCRef
patchName, SCList selFiles, bool
(git->isStGITStack() ? "refresh top patch with" :
"amend last commit with") :
(git->isStGITStack() ? "create a new patch with" : "commit");
- QString text("Do you want to " + whatToDo + " the following
file(s)?\n\n" +
- selFiles.join("\n") + "\n\nwith the message:\n\n");
+ QString text("Do you want to " + whatToDo);
+
+ bool const fullList = selFiles.size() < 20;
+ if (fullList) {
+ text.append(" the following file(s)?\n\n" + selFiles.join("\n")
+ + "\n\nwith the message:\n\n");
+ } else {
+ text.append(" those " + QString::number(selFiles.size())
+ + " files the with the message:\n\n");
+ }
+
text.append(msg);
if (git->isStGITStack())
text.append("\n\nAnd patch name: " + patchName);
QTextCodec::setCodecForCStrings(tc);
- int but = QMessageBox::question(this, "Commit changes - QGit",
- text, "&Yes", "&No", QString(), 0, 1);
- return (but != 1);
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle("Commit changes - QGit");
+ msgBox.setText(text);
+ if (!fullList)
+ msgBox.setDetailedText(selFiles.join("\n"));
+ msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::Yes);
+
+ return msgBox.exec() != QMessageBox::No;
}
void CommitImpl::pushButtonSettings_clicked() {
--
1.6.0.2.1172.ga5ed0
^ permalink raw reply related
* Re: git svn: Bad URL passed to RA layer: Unrecognized URL scheme
From: Thomas Rast @ 2008-10-06 9:47 UTC (permalink / raw)
To: Jeff Kowalczyk; +Cc: git
In-Reply-To: <pan.2008.10.06.02.28.35.643705@yahoo.com>
[-- Attachment #1: Type: text/plain, Size: 776 bytes --]
Jeff Kowalczyk wrote:
> However, each operation ends with a segmentation fault. This is the case
> with all git svn repositories (e.g. several different svn remote hosts).
>
> (master) $ git svn rebase
> M django/core/management/commands/makemessages.py
> M docs/topics/i18n.txt
> r9155 = 4c86e60f62366ac9c3fd9369c17c54801a8f2ea0 (trunk)
> First, rewinding head to replay your work on top of it...
> Fast-forwarded master to refs/remotes/trunk.
> Segmentation fault
Can you try to find out what exactly segfaults here? git-svn is
implemented in Perl, so that means it's either Perl itself (unlikely),
the SVN libraries hooked into Perl, or some git command run by
git-svn.
- Thomas
--
Thomas Rast
trast@{inf,student}.ethz.ch
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: Build bug report: 'make check' needs sparse, but configure doesn't check it
From: Ed Avis @ 2008-10-06 11:28 UTC (permalink / raw)
To: git
In-Reply-To: <20081006065847.GA27516@spearce.org>
Shawn O. Pearce <spearce <at> spearce.org> writes:
>Nah. We're not going to rename the target to "make sparse".
Fair enough. Can I suggest a one line patch to avoid any misunderstanding in
the future?
diff --git a/Makefile b/Makefile
index 3c0664a..c98b921 100644
--- a/Makefile
+++ b/Makefile
@@ -1354,6 +1354,7 @@ check-sha1:: test-sha1$X
./test-sha1.sh
check: common-cmds.h
+ @echo "'make check' runs the 'sparse' tool on all source files; say
'make test' to run the test suite."
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
remove-dashes:
^ permalink raw reply related
* how to restrict commit for a repo
From: Dilip M @ 2008-10-06 12:09 UTC (permalink / raw)
To: Git Mailing List
Hi all,
I am a new user to GIT. Read few docs in web ..I want to know few things..
If I have a repository, how to prevent push from other repo's into
mine master's? I want to prevent the commits from all developers and
allow only few ppl to commit to masters..
Thanks for sharing the info on this...
--
dm
^ permalink raw reply
* Re: how to restrict commit for a repo
From: Miklos Vajna @ 2008-10-06 13:00 UTC (permalink / raw)
To: Dilip M; +Cc: Git Mailing List
In-Reply-To: <c94f8e120810060509w5eaa9138m92f1df36c9c36db6@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 308 bytes --]
On Mon, Oct 06, 2008 at 05:39:39PM +0530, Dilip M <dilipm79@gmail.com> wrote:
> If I have a repository, how to prevent push from other repo's into
> mine master's? I want to prevent the commits from all developers and
> allow only few ppl to commit to masters..
Have you seen contrib/hooks/update-paranoid?
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Repairing fatal: ref HEAD is not a symbolic ref (git checkout of svn remote)
From: Jeff Kowalczyk @ 2008-10-06 14:00 UTC (permalink / raw)
To: git
To test a particular upstream revision, on a git svn remote checkout (i.e.
branches/1.2, 76c7af2...), I checked out the equivalent of HEAD^^ (git
checkout 4a3d99c0c9).
Now back at the branch tip, I get fatal: ref HEAD is not a symbolic ref
on git svn rebase:
(4a3d99c...) $ git checkout 76c7af2
HEAD is now at 76c7af2... Minor changes to CONTRIBUTORS
(76c7af2...) $ git svn rebase
fatal: ref HEAD is not a symbolic ref Current branch HEAD is up to date.
Segmentation fault
How can I manually correct ref HEAD?
Thanks.
(The segmentation fault is a separate issue)
^ permalink raw reply
* Re: Repairing fatal: ref HEAD is not a symbolic ref (git checkout of svn remote)
From: Shawn O. Pearce @ 2008-10-06 14:02 UTC (permalink / raw)
To: Jeff Kowalczyk; +Cc: git
In-Reply-To: <pan.2008.10.06.14.00.57.104051@yahoo.com>
Jeff Kowalczyk <jtk@yahoo.com> wrote:
> To test a particular upstream revision, on a git svn remote checkout (i.e.
> branches/1.2, 76c7af2...), I checked out the equivalent of HEAD^^ (git
> checkout 4a3d99c0c9).
>
> Now back at the branch tip, I get fatal: ref HEAD is not a symbolic ref
> on git svn rebase:
>
> (4a3d99c...) $ git checkout 76c7af2
> HEAD is now at 76c7af2... Minor changes to CONTRIBUTORS
> (76c7af2...) $ git svn rebase
> fatal: ref HEAD is not a symbolic ref Current branch HEAD is up to date.
> Segmentation fault
>
> How can I manually correct ref HEAD?
git checkout whateverbranchyouwereonbefore
git svn rebase
--
Shawn.
^ permalink raw reply
* Re: [PATCH] Use "git_config_string" to simplify "remote.c" code in "handle_config"
From: Johannes Schindelin @ 2008-10-06 14:13 UTC (permalink / raw)
To: David Bryson; +Cc: git
In-Reply-To: <20081003033937.GA11594@eratosthenes.cryptobackpack.org>
Hi,
On Thu, 2 Oct 2008, David Bryson wrote:
>
> Signed-off-by: David Bryson <david@statichacks.org>
>
> I tried to keep with the naming/coding conventions that I found in
> remote.c. Feedback welcome.
>
> ---
Usually this comment goes after the --- but other than that, the form is
as perfect as you can wish for.
> @@ -314,15 +315,15 @@ static int handle_config(const char *key, const char *value, void *cb)
> return 0;
> branch = make_branch(name, subkey - name);
> if (!strcmp(subkey, ".remote")) {
> - if (!value)
> - return config_error_nonbool(key);
> - branch->remote_name = xstrdup(value);
> + if (git_config_string(&v, key, value) )
> + return -1;
> + branch->remote_name = v;
What is the reason not to write
if (git_config_string(&branch->remote_name, key, value))
return -1;
? (Also note that we do not like the space between the two closing
parentheses.)
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH] git init: --bare/--shared overrides system/global config
From: Shawn O. Pearce @ 2008-10-06 14:14 UTC (permalink / raw)
To: Deskin Miller; +Cc: git
In-Reply-To: <20081005194412.GE3052@riemann.deskinm.fdns.net>
Deskin Miller <deskinm@umich.edu> wrote:
> From b6144562983703079a1eba8cdac3506c18d751a3 Mon Sep 17 00:00:00 2001
> From: Deskin Miller <deskinm@umich.edu>
> Date: Sat, 4 Oct 2008 20:07:44 -0400
FWIW please don't include these lines in the commit message part
of the patch email. The only reason to include a "From: blah" line
(the 2nd one above) is when the name and/or email address that you
want to attribute the change to differs from the name/email that
the message is sent from. (E.g. sent from gmail.com but you want
to attribute to umich.edu.)
> If core.bare or core.sharedRepository are set in /etc/gitconfig or
> ~/.gitconfig, then 'git init' will read the values when constructing a
> new config file; [...]
> ---
Yikes.
> diff --git a/builtin-init-db.c b/builtin-init-db.c
> index 8140c12..38e282c 100644
> --- a/builtin-init-db.c
> +++ b/builtin-init-db.c
> @@ -191,6 +194,8 @@ static int create_default_files(const char *template_path)
> copy_templates(template_path);
>
> git_config(git_default_config, NULL);
> + is_bare_repository_cfg = init_is_bare_repository;
> + shared_repository = init_shared_repository;
Is this really the right thing to do? It seems like it would prevent
a user from setting core.sharedRepository = group in their template
and thus always have a shared repository on their system.
I think we should only be using the command line shared option if
it was supplied, but if it was not we should be honoring what we
recieved from git_config().
However I agree that is_bare shouldn't be inherited from the config.
Its a per-repository attribute and no matter what the user asked
for in their /etc/gitconfig or ~/.gitconfig we should correctly
set it for this current repository.
> diff --git a/t/t0001-init.sh b/t/t0001-init.sh
> index 620da5b..6a6bca0 100755
> --- a/t/t0001-init.sh
> +++ b/t/t0001-init.sh
> @@ -167,4 +167,21 @@ test_expect_success 'init with --template (blank)' '
> ! test -f template-blank/.git/info/exclude
> '
>
> +test_expect_success 'init --bare/--shared overrides system/global config' '
> + (
> + HOME="`pwd`" &&
> + export HOME &&
> + test_config="$HOME"/.gitconfig &&
> + unset GIT_CONFIG_NOGLOBAL &&
> + git config -f "$test_config" core.bare false &&
> + git config -f "$test_config" core.sharedRepository 0640 &&
> + mkdir init-bare-shared-override &&
> + cd init-bare-shared-override &&
> + git init --bare --shared=0666
> + ) &&
> + check_config init-bare-shared-override true unset &&
> + test 0666 = \
> + `git config -f init-bare-shared-override/config core.sharedRepository`
> +'
A second related test would be a ~/.gitconfig which sets
core.sharedRepository = 0666 and then does "git init". I think
the right outcome is a repository which has that set.
--
Shawn.
^ permalink raw reply
* Re: [msysGit] [FYI][PATCH] Customizing the WinGit installer
From: Johannes Schindelin @ 2008-10-06 14:22 UTC (permalink / raw)
To: Petr Baudis; +Cc: msysgit, git
In-Reply-To: <20081003122727.GE10360@machine.or.cz>
Hi,
On Fri, 3 Oct 2008, Petr Baudis wrote:
> -InfoBeforeFile=gpl-2.0.rtf
I'd rather keep it in, especially in a corporate environment.
Ciao,
Dscho
^ permalink raw reply
* Re: [msysGit] [FYI][PATCH] Customizing the WinGit installer
From: Petr Baudis @ 2008-10-06 14:18 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: msysgit, git
In-Reply-To: <alpine.DEB.1.00.0810061621110.22125@pacific.mpi-cbg.de.mpi-cbg.de>
Hi,
On Mon, Oct 06, 2008 at 04:22:08PM +0200, Johannes Schindelin wrote:
> On Fri, 3 Oct 2008, Petr Baudis wrote:
>
> > -InfoBeforeFile=gpl-2.0.rtf
>
> I'd rather keep it in, especially in a corporate environment.
why? How is it relevant for the users?
(Corporate environment or not, I actually never understood showing GPL
in the installers. It always seemed to me that people just feel the need
to fill the space where the EULA usually appears. ;-)
Petr "Pasky" Baudis
^ permalink raw reply
* Re: [PATCH] builtin-commit: avoid always using reduce_heads()
From: Shawn O. Pearce @ 2008-10-06 14:19 UTC (permalink / raw)
To: Miklos Vajna; +Cc: jnareb, Johannes.Schindelin, git, SZEDER GGGbor
In-Reply-To: <1223236300-2170-1-git-send-email-vmiklos@frugalware.org>
Miklos Vajna <vmiklos@frugalware.org> wrote:
> In case git merge --no-ff is used with --no-commit or we have a
> conflict, write info about if fast forwards are allowed or not to
> $GIT_DIR/MERGE_MODE.
> On Fri, Oct 03, 2008 at 07:59:15AM -0700, "Shawn O. Pearce" <spearce@spearce.org> wrote:
> > ./test-lib.sh: line 237: 362 Segmentation fault git commit -m
> > initial
> > * FAIL 1: setup
> >
> > I leave the debugging to you. ;-)
>
> That's weird, make test passed for me before I sent this patch. It was
> based on 15dc66a.
>
> Now I squashed in Gabor's patch and rebased it against 52e8370, I hope
> it fixed the issue (make test still passes for me).
Yea, Gabor's patch squashed in is what was needed. This is already
in next; it was there on Friday. But thanks anyway.
--
Shawn.
^ permalink raw reply
* Re: [PATCH] rebase --no-verify
From: Shawn O. Pearce @ 2008-10-06 14:30 UTC (permalink / raw)
To: Nanako Shiraishi; +Cc: git
In-Reply-To: <20081006141429.6117@nanako3.lavabit.com>
Nanako Shiraishi <nanako3@lavabit.com> wrote:
> It is sometimes desirable to disable the safety net of pre-rebase hook
> when the user knows what he is doing (for example, when the original
> changes on the branch have not been shown to the public yet).
>
> This teaches --no-verify option to git-rebase, which is similar to the way
> pre-commit hook is bypassed by git-commit.
Looks good.
> It probably is better to fix "rebase -i" to share more code with the main
> "rebase" script to avoid duplicated run-pre-rebase-hook function, but it
> is beyond what I can do right now. Perhaps people more smart and
> beautiful than me can help (^_^;)
True. But its already a mess. git-sequencer is probably the
right approach to merge it all together.
> git-rebase--interactive.sh | 10 +++++++++-
> git-rebase.sh | 7 ++++++-
> t/t3409-rebase-hook.sh | 16 ++++++++++++++++
> 3 files changed, 31 insertions(+), 2 deletions(-)
Docs?
--
Shawn.
^ permalink raw reply
* [PATCH 1/2] add--interactive: rename [r]evert command to [r]eset
From: Marc Weber @ 2008-10-06 14:46 UTC (permalink / raw)
To: git; +Cc: Shawn Pearce
In-Reply-To: <20081005221150.GA22007@coredump.intra.peff.net>
The term "revert" is used very confusingly here. In git, to "revert"
is to take the changes made by a previous commit and un-apply them,
creating a new commit. The act of changing what is in the index to
match HEAD is called "reset".
Furthermore, incoming svn users will also find this confusing, since
to them, "revert" means to reset the working tree, which corresponds
to "checkout" in git.
Signed-off-by: Marc Weber <marco-oweber@gmx.de>
commit message rewritten by: Jeff King <peff@peff.net>
---
git-add--interactive.perl | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index da768ee..5352d16 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -498,8 +498,8 @@ sub update_cmd {
print "\n";
}
-sub revert_cmd {
- my @update = list_and_choose({ PROMPT => 'Revert',
+sub reset_cmd {
+ my @update = list_and_choose({ PROMPT => 'Reset',
HEADER => $status_head, },
list_modified());
if (@update) {
@@ -527,7 +527,7 @@ sub revert_cmd {
}
}
refresh();
- say_n_paths('reverted', @update);
+ say_n_paths('reset', @update);
}
print "\n";
}
@@ -1046,7 +1046,7 @@ sub help_cmd {
print colored $help_color, <<\EOF ;
status - show paths with changes
update - add working tree state to the staged set of changes
-revert - revert staged set of changes back to the HEAD version
+reset - reset staged set of changes back to the HEAD version
patch - pick hunks and update selectively
diff - view diff between HEAD and index
add untracked - add contents of untracked files to the staged set of changes
@@ -1070,7 +1070,7 @@ sub process_args {
sub main_loop {
my @cmd = ([ 'status', \&status_cmd, ],
[ 'update', \&update_cmd, ],
- [ 'revert', \&revert_cmd, ],
+ [ 'reset', \&reset_cmd, ],
[ 'add untracked', \&add_untracked_cmd, ],
[ 'patch', \&patch_update_cmd, ],
[ 'diff', \&diff_cmd, ],
--
1.6.0.2.309.g14f93
^ permalink raw reply related
* [PATCH 2/2] add--interactive: added [c]heckout
From: Marc Weber @ 2008-10-06 14:49 UTC (permalink / raw)
To: git; +Cc: Shawn Pearce
In-Reply-To: <b41e1729a8817f0d3cba2be1edc37513a72901dd.1223304141.git.marco-oweber@gmx.de>
this is convinient for cygwin users not using rxvt.
added last (before quit and help) to not break menu layout and keep exisiting
numbering
Signed-off-by: Marc Weber <marco-oweber@gmx.de>
---
git-add--interactive | 1129 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1129 insertions(+), 0 deletions(-)
create mode 100755 git-add--interactive
diff --git a/git-add--interactive b/git-add--interactive
new file mode 100755
index 0000000..2e7fa55
--- /dev/null
+++ b/git-add--interactive
@@ -0,0 +1,1129 @@
+#!/nix/store/y0s7qlxqr35cqscfg9xy7pvvjmx1hgmn-perl-5.10.0/bin/perl -w
+use lib (split(/:/, $ENV{GITPERLLIB} || "/usr/local/lib/site_perl/5.10.0"));
+
+use strict;
+use Git;
+
+my $repo = Git->repository();
+
+my $menu_use_color = $repo->get_colorbool('color.interactive');
+my ($prompt_color, $header_color, $help_color) =
+ $menu_use_color ? (
+ $repo->get_color('color.interactive.prompt', 'bold blue'),
+ $repo->get_color('color.interactive.header', 'bold'),
+ $repo->get_color('color.interactive.help', 'red bold'),
+ ) : ();
+
+my $diff_use_color = $repo->get_colorbool('color.diff');
+my ($fraginfo_color) =
+ $diff_use_color ? (
+ $repo->get_color('color.diff.frag', 'cyan'),
+ ) : ();
+my ($diff_plain_color) =
+ $diff_use_color ? (
+ $repo->get_color('color.diff.plain', ''),
+ ) : ();
+my ($diff_old_color) =
+ $diff_use_color ? (
+ $repo->get_color('color.diff.old', 'red'),
+ ) : ();
+my ($diff_new_color) =
+ $diff_use_color ? (
+ $repo->get_color('color.diff.new', 'green'),
+ ) : ();
+
+my $normal_color = $repo->get_color("", "reset");
+
+sub colored {
+ my $color = shift;
+ my $string = join("", @_);
+
+ if (defined $color) {
+ # Put a color code at the beginning of each line, a reset at the end
+ # color after newlines that are not at the end of the string
+ $string =~ s/(\n+)(.)/$1$color$2/g;
+ # reset before newlines
+ $string =~ s/(\n+)/$normal_color$1/g;
+ # codes at beginning and end (if necessary):
+ $string =~ s/^/$color/;
+ $string =~ s/$/$normal_color/ unless $string =~ /\n$/;
+ }
+ return $string;
+}
+
+# command line options
+my $patch_mode;
+
+sub run_cmd_pipe {
+ if ($^O eq 'MSWin32' || $^O eq 'msys') {
+ my @invalid = grep {m/[":*]/} @_;
+ die "$^O does not support: @invalid\n" if @invalid;
+ my @args = map { m/ /o ? "\"$_\"": $_ } @_;
+ return qx{@args};
+ } else {
+ my $fh = undef;
+ open($fh, '-|', @_) or die;
+ return <$fh>;
+ }
+}
+
+my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
+
+if (!defined $GIT_DIR) {
+ exit(1); # rev-parse would have already said "not a git repo"
+}
+chomp($GIT_DIR);
+
+sub refresh {
+ my $fh;
+ open $fh, 'git update-index --refresh |'
+ or die;
+ while (<$fh>) {
+ ;# ignore 'needs update'
+ }
+ close $fh;
+}
+
+sub list_untracked {
+ map {
+ chomp $_;
+ $_;
+ }
+ run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
+}
+
+my $status_fmt = '%12s %12s %s';
+my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+
+{
+ my $initial;
+ sub is_initial_commit {
+ $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
+ unless defined $initial;
+ return $initial;
+ }
+}
+
+sub get_empty_tree {
+ return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+}
+
+# Returns list of hashes, contents of each of which are:
+# VALUE: pathname
+# BINARY: is a binary path
+# INDEX: is index different from HEAD?
+# FILE: is file different from index?
+# INDEX_ADDDEL: is it add/delete between HEAD and index?
+# FILE_ADDDEL: is it add/delete between index and file?
+
+sub list_modified {
+ my ($only) = @_;
+ my (%data, @return);
+ my ($add, $del, $adddel, $file);
+ my @tracked = ();
+
+ if (@ARGV) {
+ @tracked = map {
+ chomp $_; $_;
+ } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
+ return if (!@tracked);
+ }
+
+ my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+ for (run_cmd_pipe(qw(git diff-index --cached
+ --numstat --summary), $reference,
+ '--', @tracked)) {
+ if (($add, $del, $file) =
+ /^([-\d]+) ([-\d]+) (.*)/) {
+ my ($change, $bin);
+ if ($add eq '-' && $del eq '-') {
+ $change = 'binary';
+ $bin = 1;
+ }
+ else {
+ $change = "+$add/-$del";
+ }
+ $data{$file} = {
+ INDEX => $change,
+ BINARY => $bin,
+ FILE => 'nothing',
+ }
+ }
+ elsif (($adddel, $file) =
+ /^ (create|delete) mode [0-7]+ (.*)$/) {
+ $data{$file}{INDEX_ADDDEL} = $adddel;
+ }
+ }
+
+ for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {
+ if (($add, $del, $file) =
+ /^([-\d]+) ([-\d]+) (.*)/) {
+ if (!exists $data{$file}) {
+ $data{$file} = +{
+ INDEX => 'unchanged',
+ BINARY => 0,
+ };
+ }
+ my ($change, $bin);
+ if ($add eq '-' && $del eq '-') {
+ $change = 'binary';
+ $bin = 1;
+ }
+ else {
+ $change = "+$add/-$del";
+ }
+ $data{$file}{FILE} = $change;
+ if ($bin) {
+ $data{$file}{BINARY} = 1;
+ }
+ }
+ elsif (($adddel, $file) =
+ /^ (create|delete) mode [0-7]+ (.*)$/) {
+ $data{$file}{FILE_ADDDEL} = $adddel;
+ }
+ }
+
+ for (sort keys %data) {
+ my $it = $data{$_};
+
+ if ($only) {
+ if ($only eq 'index-only') {
+ next if ($it->{INDEX} eq 'unchanged');
+ }
+ if ($only eq 'file-only') {
+ next if ($it->{FILE} eq 'nothing');
+ }
+ }
+ push @return, +{
+ VALUE => $_,
+ %$it,
+ };
+ }
+ return @return;
+}
+
+sub find_unique {
+ my ($string, @stuff) = @_;
+ my $found = undef;
+ for (my $i = 0; $i < @stuff; $i++) {
+ my $it = $stuff[$i];
+ my $hit = undef;
+ if (ref $it) {
+ if ((ref $it) eq 'ARRAY') {
+ $it = $it->[0];
+ }
+ else {
+ $it = $it->{VALUE};
+ }
+ }
+ eval {
+ if ($it =~ /^$string/) {
+ $hit = 1;
+ };
+ };
+ if (defined $hit && defined $found) {
+ return undef;
+ }
+ if ($hit) {
+ $found = $i + 1;
+ }
+ }
+ return $found;
+}
+
+# inserts string into trie and updates count for each character
+sub update_trie {
+ my ($trie, $string) = @_;
+ foreach (split //, $string) {
+ $trie = $trie->{$_} ||= {COUNT => 0};
+ $trie->{COUNT}++;
+ }
+}
+
+# returns an array of tuples (prefix, remainder)
+sub find_unique_prefixes {
+ my @stuff = @_;
+ my @return = ();
+
+ # any single prefix exceeding the soft limit is omitted
+ # if any prefix exceeds the hard limit all are omitted
+ # 0 indicates no limit
+ my $soft_limit = 0;
+ my $hard_limit = 3;
+
+ # build a trie modelling all possible options
+ my %trie;
+ foreach my $print (@stuff) {
+ if ((ref $print) eq 'ARRAY') {
+ $print = $print->[0];
+ }
+ elsif ((ref $print) eq 'HASH') {
+ $print = $print->{VALUE};
+ }
+ update_trie(\%trie, $print);
+ push @return, $print;
+ }
+
+ # use the trie to find the unique prefixes
+ for (my $i = 0; $i < @return; $i++) {
+ my $ret = $return[$i];
+ my @letters = split //, $ret;
+ my %search = %trie;
+ my ($prefix, $remainder);
+ my $j;
+ for ($j = 0; $j < @letters; $j++) {
+ my $letter = $letters[$j];
+ if ($search{$letter}{COUNT} == 1) {
+ $prefix = substr $ret, 0, $j + 1;
+ $remainder = substr $ret, $j + 1;
+ last;
+ }
+ else {
+ my $prefix = substr $ret, 0, $j;
+ return ()
+ if ($hard_limit && $j + 1 > $hard_limit);
+ }
+ %search = %{$search{$letter}};
+ }
+ if ($soft_limit && $j + 1 > $soft_limit) {
+ $prefix = undef;
+ $remainder = $ret;
+ }
+ $return[$i] = [$prefix, $remainder];
+ }
+ return @return;
+}
+
+# filters out prefixes which have special meaning to list_and_choose()
+sub is_valid_prefix {
+ my $prefix = shift;
+ return (defined $prefix) &&
+ !($prefix =~ /[\s,]/) && # separators
+ !($prefix =~ /^-/) && # deselection
+ !($prefix =~ /^\d+/) && # selection
+ ($prefix ne '*') && # "all" wildcard
+ ($prefix ne '?'); # prompt help
+}
+
+# given a prefix/remainder tuple return a string with the prefix highlighted
+# for now use square brackets; later might use ANSI colors (underline, bold)
+sub highlight_prefix {
+ my $prefix = shift;
+ my $remainder = shift;
+
+ if (!defined $prefix) {
+ return $remainder;
+ }
+
+ if (!is_valid_prefix($prefix)) {
+ return "$prefix$remainder";
+ }
+
+ if (!$menu_use_color) {
+ return "[$prefix]$remainder";
+ }
+
+ return "$prompt_color$prefix$normal_color$remainder";
+}
+
+sub list_and_choose {
+ my ($opts, @stuff) = @_;
+ my (@chosen, @return);
+ my $i;
+ my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};
+
+ TOPLOOP:
+ while (1) {
+ my $last_lf = 0;
+
+ if ($opts->{HEADER}) {
+ if (!$opts->{LIST_FLAT}) {
+ print " ";
+ }
+ print colored $header_color, "$opts->{HEADER}\n";
+ }
+ for ($i = 0; $i < @stuff; $i++) {
+ my $chosen = $chosen[$i] ? '*' : ' ';
+ my $print = $stuff[$i];
+ my $ref = ref $print;
+ my $highlighted = highlight_prefix(@{$prefixes[$i]})
+ if @prefixes;
+ if ($ref eq 'ARRAY') {
+ $print = $highlighted || $print->[0];
+ }
+ elsif ($ref eq 'HASH') {
+ my $value = $highlighted || $print->{VALUE};
+ $print = sprintf($status_fmt,
+ $print->{INDEX},
+ $print->{FILE},
+ $value);
+ }
+ else {
+ $print = $highlighted || $print;
+ }
+ printf("%s%2d: %s", $chosen, $i+1, $print);
+ if (($opts->{LIST_FLAT}) &&
+ (($i + 1) % ($opts->{LIST_FLAT}))) {
+ print "\t";
+ $last_lf = 0;
+ }
+ else {
+ print "\n";
+ $last_lf = 1;
+ }
+ }
+ if (!$last_lf) {
+ print "\n";
+ }
+
+ return if ($opts->{LIST_ONLY});
+
+ print colored $prompt_color, $opts->{PROMPT};
+ if ($opts->{SINGLETON}) {
+ print "> ";
+ }
+ else {
+ print ">> ";
+ }
+ my $line = <STDIN>;
+ if (!$line) {
+ print "\n";
+ $opts->{ON_EOF}->() if $opts->{ON_EOF};
+ last;
+ }
+ chomp $line;
+ last if $line eq '';
+ if ($line eq '?') {
+ $opts->{SINGLETON} ?
+ singleton_prompt_help_cmd() :
+ prompt_help_cmd();
+ next TOPLOOP;
+ }
+ for my $choice (split(/[\s,]+/, $line)) {
+ my $choose = 1;
+ my ($bottom, $top);
+
+ # Input that begins with '-'; unchoose
+ if ($choice =~ s/^-//) {
+ $choose = 0;
+ }
+ # A range can be specified like 5-7 or 5-.
+ if ($choice =~ /^(\d+)-(\d*)$/) {
+ ($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);
+ }
+ elsif ($choice =~ /^\d+$/) {
+ $bottom = $top = $choice;
+ }
+ elsif ($choice eq '*') {
+ $bottom = 1;
+ $top = 1 + @stuff;
+ }
+ else {
+ $bottom = $top = find_unique($choice, @stuff);
+ if (!defined $bottom) {
+ print "Huh ($choice)?\n";
+ next TOPLOOP;
+ }
+ }
+ if ($opts->{SINGLETON} && $bottom != $top) {
+ print "Huh ($choice)?\n";
+ next TOPLOOP;
+ }
+ for ($i = $bottom-1; $i <= $top-1; $i++) {
+ next if (@stuff <= $i || $i < 0);
+ $chosen[$i] = $choose;
+ }
+ }
+ last if ($opts->{IMMEDIATE} || $line eq '*');
+ }
+ for ($i = 0; $i < @stuff; $i++) {
+ if ($chosen[$i]) {
+ push @return, $stuff[$i];
+ }
+ }
+ return @return;
+}
+
+sub singleton_prompt_help_cmd {
+ print colored $help_color, <<\EOF ;
+Prompt help:
+1 - select a numbered item
+foo - select item based on unique prefix
+ - (empty) select nothing
+EOF
+}
+
+sub prompt_help_cmd {
+ print colored $help_color, <<\EOF ;
+Prompt help:
+1 - select a single item
+3-5 - select a range of items
+2-3,6-9 - select multiple ranges
+foo - select item based on unique prefix
+-... - unselect specified items
+* - choose all items
+ - (empty) finish selecting
+EOF
+}
+
+sub status_cmd {
+ list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
+ list_modified());
+ print "\n";
+}
+
+sub say_n_paths {
+ my $did = shift @_;
+ my $cnt = scalar @_;
+ print "$did ";
+ if (1 < $cnt) {
+ print "$cnt paths\n";
+ }
+ else {
+ print "one path\n";
+ }
+}
+
+sub update_cmd {
+ my @mods = list_modified('file-only');
+ return if (!@mods);
+
+ my @update = list_and_choose({ PROMPT => 'Update',
+ HEADER => $status_head, },
+ @mods);
+ if (@update) {
+ system(qw(git update-index --add --remove --),
+ map { $_->{VALUE} } @update);
+ say_n_paths('updated', @update);
+ }
+ print "\n";
+}
+
+sub reset_cmd {
+ my @update = list_and_choose({ PROMPT => 'Reset',
+ HEADER => $status_head, },
+ list_modified());
+ if (@update) {
+ if (is_initial_commit()) {
+ system(qw(git rm --cached),
+ map { $_->{VALUE} } @update);
+ }
+ else {
+ my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
+ map { $_->{VALUE} } @update);
+ my $fh;
+ open $fh, '| git update-index --index-info'
+ or die;
+ for (@lines) {
+ print $fh $_;
+ }
+ close($fh);
+ for (@update) {
+ if ($_->{INDEX_ADDDEL} &&
+ $_->{INDEX_ADDDEL} eq 'create') {
+ system(qw(git update-index --force-remove --),
+ $_->{VALUE});
+ print "note: $_->{VALUE} is untracked now.\n";
+ }
+ }
+ }
+ refresh();
+ say_n_paths('reset', @update);
+ }
+ print "\n";
+}
+
+sub checkout_cmd {
+ my @update = list_and_choose({ PROMPT => 'Checkout',
+ HEADER => $status_head, },
+ list_modified());
+ if (@update) {
+ if (is_initial_commit()) {
+ # should never be executed because there can't be modified files
+ print "error: no revision in repo yet\n";
+ }
+ else {
+ for (@update) {
+ system(qw(git checkout --),
+ $_->{VALUE});
+ }
+ }
+ refresh();
+ say_n_paths('checked out', @update);
+ }
+ print "\n";
+}
+
+sub add_untracked_cmd {
+ my @add = list_and_choose({ PROMPT => 'Add untracked' },
+ list_untracked());
+ if (@add) {
+ system(qw(git update-index --add --), @add);
+ say_n_paths('added', @add);
+ }
+ print "\n";
+}
+
+sub parse_diff {
+ my ($path) = @_;
+ my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+ my @colored = ();
+ if ($diff_use_color) {
+ @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
+ }
+ my (@hunk) = { TEXT => [], DISPLAY => [] };
+
+ for (my $i = 0; $i < @diff; $i++) {
+ if ($diff[$i] =~ /^@@ /) {
+ push @hunk, { TEXT => [], DISPLAY => [] };
+ }
+ push @{$hunk[-1]{TEXT}}, $diff[$i];
+ push @{$hunk[-1]{DISPLAY}},
+ ($diff_use_color ? $colored[$i] : $diff[$i]);
+ }
+ return @hunk;
+}
+
+sub parse_diff_header {
+ my $src = shift;
+
+ my $head = { TEXT => [], DISPLAY => [] };
+ my $mode = { TEXT => [], DISPLAY => [] };
+
+ for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
+ my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
+ $mode : $head;
+ push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
+ push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
+ }
+ return ($head, $mode);
+}
+
+sub hunk_splittable {
+ my ($text) = @_;
+
+ my @s = split_hunk($text);
+ return (1 < @s);
+}
+
+sub parse_hunk_header {
+ my ($line) = @_;
+ my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
+ $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
+ $o_cnt = 1 unless defined $o_cnt;
+ $n_cnt = 1 unless defined $n_cnt;
+ return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
+}
+
+sub split_hunk {
+ my ($text, $display) = @_;
+ my @split = ();
+ if (!defined $display) {
+ $display = $text;
+ }
+ # If there are context lines in the middle of a hunk,
+ # it can be split, but we would need to take care of
+ # overlaps later.
+
+ my ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);
+ my $hunk_start = 1;
+
+ OUTER:
+ while (1) {
+ my $next_hunk_start = undef;
+ my $i = $hunk_start - 1;
+ my $this = +{
+ TEXT => [],
+ DISPLAY => [],
+ OLD => $o_ofs,
+ NEW => $n_ofs,
+ OCNT => 0,
+ NCNT => 0,
+ ADDDEL => 0,
+ POSTCTX => 0,
+ USE => undef,
+ };
+
+ while (++$i < @$text) {
+ my $line = $text->[$i];
+ my $display = $display->[$i];
+ if ($line =~ /^ /) {
+ if ($this->{ADDDEL} &&
+ !defined $next_hunk_start) {
+ # We have seen leading context and
+ # adds/dels and then here is another
+ # context, which is trailing for this
+ # split hunk and leading for the next
+ # one.
+ $next_hunk_start = $i;
+ }
+ push @{$this->{TEXT}}, $line;
+ push @{$this->{DISPLAY}}, $display;
+ $this->{OCNT}++;
+ $this->{NCNT}++;
+ if (defined $next_hunk_start) {
+ $this->{POSTCTX}++;
+ }
+ next;
+ }
+
+ # add/del
+ if (defined $next_hunk_start) {
+ # We are done with the current hunk and
+ # this is the first real change for the
+ # next split one.
+ $hunk_start = $next_hunk_start;
+ $o_ofs = $this->{OLD} + $this->{OCNT};
+ $n_ofs = $this->{NEW} + $this->{NCNT};
+ $o_ofs -= $this->{POSTCTX};
+ $n_ofs -= $this->{POSTCTX};
+ push @split, $this;
+ redo OUTER;
+ }
+ push @{$this->{TEXT}}, $line;
+ push @{$this->{DISPLAY}}, $display;
+ $this->{ADDDEL}++;
+ if ($line =~ /^-/) {
+ $this->{OCNT}++;
+ }
+ else {
+ $this->{NCNT}++;
+ }
+ }
+
+ push @split, $this;
+ last;
+ }
+
+ for my $hunk (@split) {
+ $o_ofs = $hunk->{OLD};
+ $n_ofs = $hunk->{NEW};
+ my $o_cnt = $hunk->{OCNT};
+ my $n_cnt = $hunk->{NCNT};
+
+ my $head = ("@@ -$o_ofs" .
+ (($o_cnt != 1) ? ",$o_cnt" : '') .
+ " +$n_ofs" .
+ (($n_cnt != 1) ? ",$n_cnt" : '') .
+ " @@\n");
+ my $display_head = $head;
+ unshift @{$hunk->{TEXT}}, $head;
+ if ($diff_use_color) {
+ $display_head = colored($fraginfo_color, $head);
+ }
+ unshift @{$hunk->{DISPLAY}}, $display_head;
+ }
+ return @split;
+}
+
+
+sub color_diff {
+ return map {
+ colored((/^@/ ? $fraginfo_color :
+ /^\+/ ? $diff_new_color :
+ /^-/ ? $diff_old_color :
+ $diff_plain_color),
+ $_);
+ } @_;
+}
+
+sub edit_hunk_manually {
+ my ($oldtext) = @_;
+
+ my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
+ my $fh;
+ open $fh, '>', $hunkfile
+ or die "failed to open hunk edit file for writing: " . $!;
+ print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+ print $fh @$oldtext;
+ print $fh <<EOF;
+# ---
+# To remove '-' lines, make them ' ' lines (context).
+# To remove '+' lines, delete them.
+# Lines starting with # will be removed.
+#
+# If the patch applies cleanly, the edited hunk will immediately be
+# marked for staging. If it does not apply cleanly, you will be given
+# an opportunity to edit again. If all lines of the hunk are removed,
+# then the edit is aborted and the hunk is left unchanged.
+EOF
+ close $fh;
+
+ my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor")
+ || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+ system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
+
+ open $fh, '<', $hunkfile
+ or die "failed to open hunk edit file for reading: " . $!;
+ my @newtext = grep { !/^#/ } <$fh>;
+ close $fh;
+ unlink $hunkfile;
+
+ # Abort if nothing remains
+ if (!grep { /\S/ } @newtext) {
+ return undef;
+ }
+
+ # Reinsert the first hunk header if the user accidentally deleted it
+ if ($newtext[0] !~ /^@/) {
+ unshift @newtext, $oldtext->[0];
+ }
+ return \@newtext;
+}
+
+sub diff_applies {
+ my $fh;
+ open $fh, '| git apply --recount --cached --check';
+ for my $h (@_) {
+ print $fh @{$h->{TEXT}};
+ }
+ return close $fh;
+}
+
+sub prompt_yesno {
+ my ($prompt) = @_;
+ while (1) {
+ print colored $prompt_color, $prompt;
+ my $line = <STDIN>;
+ return 0 if $line =~ /^n/i;
+ return 1 if $line =~ /^y/i;
+ }
+}
+
+sub edit_hunk_loop {
+ my ($head, $hunk, $ix) = @_;
+ my $text = $hunk->[$ix]->{TEXT};
+
+ while (1) {
+ $text = edit_hunk_manually($text);
+ if (!defined $text) {
+ return undef;
+ }
+ my $newhunk = { TEXT => $text, USE => 1 };
+ if (diff_applies($head,
+ @{$hunk}[0..$ix-1],
+ $newhunk,
+ @{$hunk}[$ix+1..$#{$hunk}])) {
+ $newhunk->{DISPLAY} = [color_diff(@{$text})];
+ return $newhunk;
+ }
+ else {
+ prompt_yesno(
+ 'Your edited hunk does not apply. Edit again '
+ . '(saying "no" discards!) [y/n]? '
+ ) or return undef;
+ }
+ }
+}
+
+sub help_patch_cmd {
+ print colored $help_color, <<\EOF ;
+y - stage this hunk
+n - do not stage this hunk
+a - stage this and all the remaining hunks in the file
+d - do not stage this hunk nor any of the remaining hunks in the file
+j - leave this hunk undecided, see next undecided hunk
+J - leave this hunk undecided, see next hunk
+k - leave this hunk undecided, see previous undecided hunk
+K - leave this hunk undecided, see previous hunk
+s - split the current hunk into smaller hunks
+e - manually edit the current hunk
+? - print help
+EOF
+}
+
+sub patch_update_cmd {
+ my @mods = grep { !($_->{BINARY}) } list_modified('file-only');
+ my @them;
+
+ if (!@mods) {
+ print STDERR "No changes.\n";
+ return 0;
+ }
+ if ($patch_mode) {
+ @them = @mods;
+ }
+ else {
+ @them = list_and_choose({ PROMPT => 'Patch update',
+ HEADER => $status_head, },
+ @mods);
+ }
+ for (@them) {
+ patch_update_file($_->{VALUE});
+ }
+}
+
+sub patch_update_file {
+ my ($ix, $num);
+ my $path = shift;
+ my ($head, @hunk) = parse_diff($path);
+ ($head, my $mode) = parse_diff_header($head);
+ for (@{$head->{DISPLAY}}) {
+ print;
+ }
+
+ if (@{$mode->{TEXT}}) {
+ while (1) {
+ print @{$mode->{DISPLAY}};
+ print colored $prompt_color,
+ "Stage mode change [y/n/a/d/?]? ";
+ my $line = <STDIN>;
+ if ($line =~ /^y/i) {
+ $mode->{USE} = 1;
+ last;
+ }
+ elsif ($line =~ /^n/i) {
+ $mode->{USE} = 0;
+ last;
+ }
+ elsif ($line =~ /^a/i) {
+ $_->{USE} = 1 foreach ($mode, @hunk);
+ last;
+ }
+ elsif ($line =~ /^d/i) {
+ $_->{USE} = 0 foreach ($mode, @hunk);
+ last;
+ }
+ else {
+ help_patch_cmd('');
+ next;
+ }
+ }
+ }
+
+ $num = scalar @hunk;
+ $ix = 0;
+
+ while (1) {
+ my ($prev, $next, $other, $undecided, $i);
+ $other = '';
+
+ if ($num <= $ix) {
+ $ix = 0;
+ }
+ for ($i = 0; $i < $ix; $i++) {
+ if (!defined $hunk[$i]{USE}) {
+ $prev = 1;
+ $other .= '/k';
+ last;
+ }
+ }
+ if ($ix) {
+ $other .= '/K';
+ }
+ for ($i = $ix + 1; $i < $num; $i++) {
+ if (!defined $hunk[$i]{USE}) {
+ $next = 1;
+ $other .= '/j';
+ last;
+ }
+ }
+ if ($ix < $num - 1) {
+ $other .= '/J';
+ }
+ for ($i = 0; $i < $num; $i++) {
+ if (!defined $hunk[$i]{USE}) {
+ $undecided = 1;
+ last;
+ }
+ }
+ last if (!$undecided);
+
+ if (hunk_splittable($hunk[$ix]{TEXT})) {
+ $other .= '/s';
+ }
+ $other .= '/e';
+ for (@{$hunk[$ix]{DISPLAY}}) {
+ print;
+ }
+ print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
+ my $line = <STDIN>;
+ if ($line) {
+ if ($line =~ /^y/i) {
+ $hunk[$ix]{USE} = 1;
+ }
+ elsif ($line =~ /^n/i) {
+ $hunk[$ix]{USE} = 0;
+ }
+ elsif ($line =~ /^a/i) {
+ while ($ix < $num) {
+ if (!defined $hunk[$ix]{USE}) {
+ $hunk[$ix]{USE} = 1;
+ }
+ $ix++;
+ }
+ next;
+ }
+ elsif ($line =~ /^d/i) {
+ while ($ix < $num) {
+ if (!defined $hunk[$ix]{USE}) {
+ $hunk[$ix]{USE} = 0;
+ }
+ $ix++;
+ }
+ next;
+ }
+ elsif ($other =~ /K/ && $line =~ /^K/) {
+ $ix--;
+ next;
+ }
+ elsif ($other =~ /J/ && $line =~ /^J/) {
+ $ix++;
+ next;
+ }
+ elsif ($other =~ /k/ && $line =~ /^k/) {
+ while (1) {
+ $ix--;
+ last if (!$ix ||
+ !defined $hunk[$ix]{USE});
+ }
+ next;
+ }
+ elsif ($other =~ /j/ && $line =~ /^j/) {
+ while (1) {
+ $ix++;
+ last if ($ix >= $num ||
+ !defined $hunk[$ix]{USE});
+ }
+ next;
+ }
+ elsif ($other =~ /s/ && $line =~ /^s/) {
+ my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
+ if (1 < @split) {
+ print colored $header_color, "Split into ",
+ scalar(@split), " hunks.\n";
+ }
+ splice (@hunk, $ix, 1, @split);
+ $num = scalar @hunk;
+ next;
+ }
+ elsif ($line =~ /^e/) {
+ my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
+ if (defined $newhunk) {
+ splice @hunk, $ix, 1, $newhunk;
+ }
+ }
+ else {
+ help_patch_cmd($other);
+ next;
+ }
+ # soft increment
+ while (1) {
+ $ix++;
+ last if ($ix >= $num ||
+ !defined $hunk[$ix]{USE});
+ }
+ }
+ }
+
+ my $n_lofs = 0;
+ my @result = ();
+ if ($mode->{USE}) {
+ push @result, @{$mode->{TEXT}};
+ }
+ for (@hunk) {
+ if ($_->{USE}) {
+ push @result, @{$_->{TEXT}};
+ }
+ }
+
+ if (@result) {
+ my $fh;
+
+ open $fh, '| git apply --cached --recount';
+ for (@{$head->{TEXT}}, @result) {
+ print $fh $_;
+ }
+ if (!close $fh) {
+ for (@{$head->{TEXT}}, @result) {
+ print STDERR $_;
+ }
+ }
+ refresh();
+ }
+
+ print "\n";
+}
+
+sub diff_cmd {
+ my @mods = list_modified('index-only');
+ @mods = grep { !($_->{BINARY}) } @mods;
+ return if (!@mods);
+ my (@them) = list_and_choose({ PROMPT => 'Review diff',
+ IMMEDIATE => 1,
+ HEADER => $status_head, },
+ @mods);
+ return if (!@them);
+ my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+ system(qw(git diff -p --cached), $reference, '--',
+ map { $_->{VALUE} } @them);
+}
+
+sub quit_cmd {
+ print "Bye.\n";
+ exit(0);
+}
+
+sub help_cmd {
+ print colored $help_color, <<\EOF ;
+status - show paths with changes
+update - add working tree state to the staged set of changes
+reset - reset staged set of changes back to the HEAD version
+patch - pick hunks and update selectively
+diff - view diff between HEAD and index
+add untracked - add contents of untracked files to the staged set of changes
+EOF
+}
+
+sub process_args {
+ return unless @ARGV;
+ my $arg = shift @ARGV;
+ if ($arg eq "--patch") {
+ $patch_mode = 1;
+ $arg = shift @ARGV or die "missing --";
+ die "invalid argument $arg, expecting --"
+ unless $arg eq "--";
+ }
+ elsif ($arg ne "--") {
+ die "invalid argument $arg, expecting --";
+ }
+}
+
+sub main_loop {
+ my @cmd = ([ 'status', \&status_cmd, ],
+ [ 'update', \&update_cmd, ],
+ [ 'reset', \&reset_cmd, ],
+ [ 'add untracked', \&add_untracked_cmd, ],
+ [ 'patch', \&patch_update_cmd, ],
+ [ 'diff', \&diff_cmd, ],
+ [ 'checkout', \&checkout_cmd, ],
+ [ 'quit', \&quit_cmd, ],
+ [ 'help', \&help_cmd, ],
+ );
+ while (1) {
+ my ($it) = list_and_choose({ PROMPT => 'What now',
+ SINGLETON => 1,
+ LIST_FLAT => 4,
+ HEADER => '*** Commands ***',
+ ON_EOF => \&quit_cmd,
+ IMMEDIATE => 1 }, @cmd);
+ if ($it) {
+ eval {
+ $it->[1]->();
+ };
+ if ($@) {
+ print "$@";
+ }
+ }
+ }
+}
+
+process_args();
+refresh();
+if ($patch_mode) {
+ patch_update_cmd();
+}
+else {
+ status_cmd();
+ main_loop();
+}
--
1.6.0.2.309.g14f93
^ permalink raw reply related
* Re: [PATCH 2/2] add--interactive: added [c]heckout
From: Shawn O. Pearce @ 2008-10-06 14:51 UTC (permalink / raw)
To: Marc Weber; +Cc: git
In-Reply-To: <353e718308129444268e61f251dab769761f095f.1223304141.git.marco-oweber@gmx.de>
Marc Weber <marco-oweber@gmx.de> wrote:
> this is convinient for cygwin users not using rxvt.
>
> added last (before quit and help) to not break menu layout and keep exisiting
> numbering
>
> Signed-off-by: Marc Weber <marco-oweber@gmx.de>
> ---
> git-add--interactive | 1129 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 1129 insertions(+), 0 deletions(-)
> create mode 100755 git-add--interactive
>
> diff --git a/git-add--interactive b/git-add--interactive
> new file mode 100755
> index 0000000..2e7fa55
> --- /dev/null
> +++ b/git-add--interactive
Uhm, what is this patch against? git.git? If so why are we creating
a new 1129 line script named something erily close to a script we
already have?
--
Shawn.
^ permalink raw reply
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