* Re: [PATCH v2] user-manual: add advanced topic "bisecting merges"
From: Benoit Sigoure @ 2007-11-08 14:51 UTC (permalink / raw)
To: Steffen Prohaska
Cc: Johannes Sixt, Andreas Ericsson, Ralf Wildenhues,
Git Mailing List
In-Reply-To: <97F64156-A457-4BC1-84BE-108369FFD18C@zib.de>
[-- Attachment #1: Type: text/plain, Size: 1490 bytes --]
On Nov 8, 2007, at 1:54 PM, Steffen Prohaska wrote:
> Do you use rebase like this in real life?
>
> I thought of the text as background information that might
> be helpful for users who want do decide wether to merge or
> to rebase. The problem described may be valuable information
> supporting a decision about a recommended workflow for a group
> of users.
You're missing the point. Johannes suggested that you rebase *only*
for bisecting purpose. Once you find the culprit commit, throw away
your rebased stuff. I've never thought about doing this myself, but
it's a very clever way of tackling this problem. It's slightly less
convenient if you need to bisect a large portion of the history (that
involves many branches and merges) because in this case we'd like to
have a magic git-linearize-history <start-treeish> <end-treeish>.
Unless this is already easily doable with git-rebase?
So to summarize (untested):
git merge topic
make check
<omg something's broken>
git reset --hard HEAD~1 # undo the merge
git checkout -b wtf
git rebase topic master
git bisect ...
<OK I found the culprit>
git checkout master
git branch -D wtf
git merge --no-commit topic
<fix the problem>
git commit # merge done, semantic glitch fixed
Correct me if I'm wrong.
This has *nothing* to do with the fact that you use merge or rebase
to do whatever you were doing.
--
Benoit Sigoure aka Tsuna
EPITA Research and Development Laboratory
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 186 bytes --]
^ permalink raw reply
* [PATCH] Remove deprecated commands from command list and update manpages
From: Jonas Fonseca @ 2007-11-08 14:54 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0711080041120.4362@racer.site>
... and remove manpages of commands that no longer exists.
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
---
Documentation/cmd-list.perl | 5 ---
Documentation/git-local-fetch.txt | 66 -------------------------------------
Documentation/git-ssh-fetch.txt | 52 -----------------------------
Documentation/git-ssh-upload.txt | 48 ---------------------------
Documentation/git-tar-tree.txt | 2 +-
Documentation/git-var.txt | 3 +-
6 files changed, 3 insertions(+), 173 deletions(-)
delete mode 100644 Documentation/git-local-fetch.txt
delete mode 100644 Documentation/git-ssh-fetch.txt
delete mode 100644 Documentation/git-ssh-upload.txt
Although this new version updates the git-lost-found(1) the command is
still listed in git(7) which I find misleading. Anyway, it made me
investigate the commands we list in git(7) and I found a few that could
possibly be cleaned up.
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 8d21d42..7f1f5d2 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -124,9 +124,7 @@ git-index-pack plumbingmanipulators
git-init mainporcelain
git-instaweb ancillaryinterrogators
gitk mainporcelain
-git-local-fetch synchingrepositories
git-log mainporcelain
-git-lost-found ancillarymanipulators
git-ls-files plumbinginterrogators
git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
@@ -178,8 +176,6 @@ git-show-branch ancillaryinterrogators
git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
git-sh-setup purehelpers
-git-ssh-fetch synchingrepositories
-git-ssh-upload synchingrepositories
git-stash mainporcelain
git-status mainporcelain
git-stripspace purehelpers
@@ -187,7 +183,6 @@ git-submodule mainporcelain
git-svn foreignscminterface
git-symbolic-ref plumbingmanipulators
git-tag mainporcelain
-git-tar-tree plumbinginterrogators
git-unpack-file plumbinginterrogators
git-unpack-objects plumbingmanipulators
git-update-index plumbingmanipulators
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
deleted file mode 100644
index e830dee..0000000
--- a/Documentation/git-local-fetch.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-git-local-fetch(1)
-==================
-
-NAME
-----
-git-local-fetch - Duplicate another git repository on a local system
-
-
-SYNOPSIS
---------
-[verse]
-'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n]
- commit-id path
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Duplicates another git repository on a local system.
-
-OPTIONS
--------
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--s::
- Instead of regular file-to-file copying use symbolic links to the objects
- in the remote repository.
--l::
- Before attempting symlinks (if -s is specified) or file-to-file copying the
- remote objects, try to hardlink the remote objects into the local
- repository.
--n::
- Never attempt to file-to-file copy remote objects. Only useful with
- -s or -l command-line options.
-
--w <filename>::
- Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
- the local end after the transfer is complete.
-
---stdin::
- Instead of a commit id on the command line (which is not expected in this
- case), 'git-local-fetch' expects lines on stdin in the format
-
- <commit-id>['\t'<filename-as-in--w>]
-
---recover::
- Verify that everything reachable from target is fetched. Used after
- an earlier fetch is interrupted.
-
-Author
-------
-Written by Junio C Hamano <junkio@cox.net>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt
deleted file mode 100644
index 8d3e2ff..0000000
--- a/Documentation/git-ssh-fetch.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-git-ssh-fetch(1)
-================
-
-NAME
-----
-git-ssh-fetch - Fetch from a remote repository over ssh connection
-
-
-
-SYNOPSIS
---------
-'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pulls from a remote repository over ssh connection, invoking
-git-ssh-upload on the other end. It functions identically to
-git-ssh-upload, aside from which end you run it on.
-
-
-OPTIONS
--------
-commit-id::
- Either the hash or the filename under [URL]/refs/ to
- pull.
-
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--w::
- Writes the commit-id into the filename under $GIT_DIR/refs/ on
- the local end after the transfer is complete.
-
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt
deleted file mode 100644
index 5e2ca8d..0000000
--- a/Documentation/git-ssh-upload.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-git-ssh-upload(1)
-=================
-
-NAME
-----
-git-ssh-upload - Push to a remote repository over ssh connection
-
-
-SYNOPSIS
---------
-'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pushes from a remote repository over ssh connection, invoking
-git-ssh-fetch on the other end. It functions identically to
-git-ssh-fetch, aside from which end you run it on.
-
-OPTIONS
--------
-commit-id::
- Id of commit to push.
-
--c::
- Get the commit objects.
--t::
- Get tree associated with the requested commit object.
--a::
- Get all the objects.
--v::
- Report what is uploaded.
--w::
- Writes the commit-id into the filename under [URL]/refs/ on
- the remote end after the transfer is complete.
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by Daniel Barkalow
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index 434607b..4aaf813 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -12,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-THIS COMMAND IS DEPRECATED. Use `git-archive` with `--format=tar`
+THIS COMMAND IS DEPRECATED. Use gitlink:git-archive[1] with `--format=tar`
option instead (and move the <base> argument to `--prefix=base/`).
Creates a tar archive containing the tree structure for the named tree.
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 8139423..73c37b0 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -20,7 +20,8 @@ OPTIONS
Cause the logical variables to be listed. In addition, all the
variables of the git configuration file .git/config are listed
as well. (However, the configuration variables listing functionality
- is deprecated in favor of `git-config -l`.)
+ is deprecated. Use gitlink:git-config[1] with the option '-l'
+ instead.)
EXAMPLE
--------
--
1.5.3.5.1623.g4aab495-dirty
--
Jonas Fonseca
^ permalink raw reply related
* Re: [PATCH v2] user-manual: add advanced topic "bisecting merges"
From: Steffen Prohaska @ 2007-11-08 14:55 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Andreas Ericsson, gitster, Ralf.Wildenhues, tsuna, git
In-Reply-To: <47330D78.3040808@viscovery.net>
On Nov 8, 2007, at 2:22 PM, Johannes Sixt wrote:
> Steffen Prohaska schrieb:
>> On Nov 8, 2007, at 10:53 AM, Johannes Sixt wrote:
>>> The text that the patch amends is about bisecting history that
>>> reveals that a merge commit breaks, which is not helpful, and
>>> then how to find where and what and why the breakage really was
>>> introduce.
>>>
>>> And the answer to "how to find" is to rebase and bisect in the
>>> rebased history.
>> Do you use rebase like this in real life?
>
> Why is this relevant?
Well, I don't want to give recommendations that are not tested
in real life. Maybe the solution turns out to be less practical
than it should be theoretically.
> You've written a superb addendum to the user manual, but IT TALKS
> ABOUT BISECTION, and is not a guideline when to use merges and when
> to rebase.
BTW, I only took what Junio wrote during a recent discussion
on the list, polished it a bit and sent it as a patch. I'm
only the editor, not the original author. That doesn't mean
I wouldn't care about the text. I'm willing to improve the text.
But all the cheers really should go to Junio.
> It better not be meant as such. Consider an integrator who has just
> merged two histories, both of which are available publically.
> Pushing out a rebased history IS NOT AN OPTION. If the poor fellow
> for the heck of it has no choice but to find the bogus commit, then
> your instructions are worth a thousand bucks - even if the rebased
> history is otherwise useless -, but any guidelines how to construct
> histories are IRRELEVANT for his case.
Ok, I see your point. I'll mention these points.
>> But now I'm wondering if your suggestions of rebasing only for
>> locating the evil commit is feasible in reality. You may need
>> to solve a lot of merge conflicts if you rebase a larger part
>> of the history. If you do not have them in your rerere cache
>> this might be time consuming. ...
>
> During the rebase you will see the same conflicts that you also had
> during the merge, even simpler ones (because they are - hopefully -
> broken down into smaller pieces). If your merge was clean (as was
> suggested in the patch), then you won't see a lot of conflicts
> during the rebase, either.
Yeah. I'll try to mention this at an appropriate place.
Steffen
^ permalink raw reply
* Re: [PATCH] Remove deprecated commands from command list and update manpages
From: Andreas Ericsson @ 2007-11-08 15:00 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: Johannes Schindelin, Junio C Hamano, git
In-Reply-To: <20071108145435.GA18727@diku.dk>
Jonas Fonseca wrote:
> ... and remove manpages of commands that no longer exists.
>
> diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
> index 8139423..73c37b0 100644
> --- a/Documentation/git-var.txt
> +++ b/Documentation/git-var.txt
> @@ -20,7 +20,8 @@ OPTIONS
> Cause the logical variables to be listed. In addition, all the
> variables of the git configuration file .git/config are listed
> as well. (However, the configuration variables listing functionality
> - is deprecated in favor of `git-config -l`.)
> + is deprecated. Use gitlink:git-config[1] with the option '-l'
> + instead.)
>
Skip the parentheses. It reads better without it.
--
Andreas Ericsson andreas.ericsson@op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
^ permalink raw reply
* Re: [PATCH 1/2] mirror pushing
From: Johannes Schindelin @ 2007-11-08 15:03 UTC (permalink / raw)
To: Andy Whitcroft; +Cc: git
In-Reply-To: <1194531890.0@pinky>
Hi,
On Thu, 8 Nov 2007, Andy Whitcroft wrote:
> - nr_refspec, refspec, args.send_all))
> + nr_refspec, refspec, args.send_all | (args.send_mirror << 1)))
This line is too long. But it needsmore love: it's all too magic to have
a 1 for send_all, and a 2 for mirror. Please introduce an enum for that
in remote.h, and use those constants, so that this hunk and the following
one cannot get out of sync that easily.
> +++ b/remote.c
> @@ -722,10 +722,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
> * without thinking.
> */
> int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
> - int nr_refspec, const char **refspec, int all)
> + int nr_refspec, const char **refspec, int flags)
> {
> struct refspec *rs =
> parse_ref_spec(nr_refspec, (const char **) refspec);
> + int send_all = flags & 01;
> + int send_mirror = flags & 02;
>
> if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
> return -1;
Thanks,
Dscho
^ permalink raw reply
* Re: [PATCH v2] user-manual: add advanced topic "bisecting merges"
From: Steffen Prohaska @ 2007-11-08 15:07 UTC (permalink / raw)
To: Benoit Sigoure
Cc: Johannes Sixt, Andreas Ericsson, Ralf Wildenhues,
Git Mailing List
In-Reply-To: <5498B368-053A-424E-B901-A55B66FF5801@lrde.epita.fr>
On Nov 8, 2007, at 3:51 PM, Benoit Sigoure wrote:
> On Nov 8, 2007, at 1:54 PM, Steffen Prohaska wrote:
>
>> Do you use rebase like this in real life?
>>
>> I thought of the text as background information that might
>> be helpful for users who want do decide wether to merge or
>> to rebase. The problem described may be valuable information
>> supporting a decision about a recommended workflow for a group
>> of users.
>
> You're missing the point. Johannes suggested that you rebase
> *only* for bisecting purpose. Once you find the culprit commit,
> throw away your rebased stuff.
I got this point. I also noted that it might be time consuming
if you need to resolve conflicts.
The original discussion in which Junio explained the problem
with bisecting merges was about workflows. The question then was
if users should _always_ rebase if possible to make bisecting
easier. It was really a workflow question.
Well, the context of the original discussion is no longer
present in the patch I sent. Therefore, Johannes' comments
absolutely make sense. Actually I find them really inspiring as
I have not thought before about rebasing temporarily, just for
finding a commit. Now I learnt that this could be a useful tool.
> I've never thought about doing this myself, but it's a very clever
> way of tackling this problem.
Apparently, you haven't thought about this solution either ;)
Steffen
^ permalink raw reply
* [PATCH] core-tutorial.txt: Fix git-show-branch example and its description
From: Sergei Organov @ 2007-11-08 15:10 UTC (permalink / raw)
To: git; +Cc: gitster
Signed-off-by: Sergei Organov <osv@javad.com>
---
The example and its description didn't match each other. In addition,
I've added explanatory notes that hopefully will decrease the chances
of confusion I've personally ran into after reading this part of the
tutorial.
Documentation/core-tutorial.txt | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 99817c5..ebd2492 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -931,12 +931,13 @@ Another useful tool, especially if you do not always work in X-Window
environment, is `git show-branch`.
------------------------------------------------
-$ git show-branch --topo-order master mybranch
+$ git-show-branch --topo-order --more=1 master mybranch
* [master] Merge work in mybranch
! [mybranch] Some work.
--
- [master] Merge work in mybranch
*+ [mybranch] Some work.
+* [master^] Some fun.
------------------------------------------------
The first two lines indicate that it is showing the two branches
@@ -954,10 +955,22 @@ because `mybranch` has not been merged to incorporate these
commits from the master branch. The string inside brackets
before the commit log message is a short name you can use to
name the commit. In the above example, 'master' and 'mybranch'
-are branch heads. 'master~1' is the first parent of 'master'
+are branch heads. 'master^' is the first parent of 'master'
branch head. Please see 'git-rev-parse' documentation if you
see more complex cases.
+[NOTE]
+Without the '--more=1' option, 'git-show-branch' would not output the
+'[master^]' commit, as '[mybranch]' commit is a common ancestor of
+both 'master' and 'mybranch' tips. Please see 'git-show-branch'
+documentation for details.
+
+[NOTE]
+If there were more commits on the 'master' branch after the merge, the
+merge commit itself would not be shown by 'git-show-branch' by
+default. You would need to provide '--sparse' option to make the
+merge commit visible in this case.
+
Now, let's pretend you are the one who did all the work in
`mybranch`, and the fruit of your hard work has finally been merged
to the `master` branch. Let's go back to `mybranch`, and run
--
1.5.3.5.529.ge3d6d
^ permalink raw reply related
* Re: [PATCH 4/3] t3700: avoid racy git situation
From: Johannes Schindelin @ 2007-11-08 15:16 UTC (permalink / raw)
To: Johannes Sixt; +Cc: git, krh, gitster
In-Reply-To: <47331E65.9010209@viscovery.net>
Hi,
On Thu, 8 Nov 2007, Johannes Sixt wrote:
> Johannes Schindelin schrieb:
> > Wow, the builtin commit is fast. It sometimes triggers a racy
> > situation in the test case for "git add --refresh -- foo".
> >
> > So when that test fails, simply sleep one second and try again.
>
> [/me looks at the calender - no, it's not April Fool's day]
No. The builtin commit is really that fast.
> Wouldn't it be better to fix git-commit (or git-add)? I would like to
> help, but you already seem to have done the analysis, so...
The problem is that the index has the same timestamp as the file "foo".
Therefore, git cannot tell if "foo" is up-to-date in the index, since it
could have been modified (and indeed is) just a fraction of a second later
than the index was last updated.
And since diff-index, as called from the test script, does not generate a
diff, but really only determines if the index information suggests that
the files are up-to-date, there is not really much you can do.
This is our good old friend, the racy git problem.
NOTE: other scms do not have this problem, mostly because they are too
slow to trigger it.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH] Remove deprecated commands from command list and update manpages
From: Johannes Schindelin @ 2007-11-08 15:18 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: Junio C Hamano, git
In-Reply-To: <20071108145435.GA18727@diku.dk>
Hi,
On Thu, 8 Nov 2007, Jonas Fonseca wrote:
> ... and remove manpages of commands that no longer exists.
>
> Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
> ---
> Documentation/cmd-list.perl | 5 ---
Maybe keep git-lost-found?
> Documentation/git-local-fetch.txt | 66 -------------------------------------
> Documentation/git-ssh-fetch.txt | 52 -----------------------------
> Documentation/git-ssh-upload.txt | 48 ---------------------------
> Documentation/git-tar-tree.txt | 2 +-
> Documentation/git-var.txt | 3 +-
Last time I checked, git-var was still alive.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH] Remove deprecated commands from command list and update manpages
From: Johannes Schindelin @ 2007-11-08 15:19 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0711081517230.4362@racer.site>
Hi,
On Thu, 8 Nov 2007, Johannes Schindelin wrote:
> On Thu, 8 Nov 2007, Jonas Fonseca wrote:
>
> > ... and remove manpages of commands that no longer exists.
> >
> > Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
> > ---
> > Documentation/cmd-list.perl | 5 ---
>
> Maybe keep git-lost-found?
>
> > Documentation/git-local-fetch.txt | 66 -------------------------------------
> > Documentation/git-ssh-fetch.txt | 52 -----------------------------
> > Documentation/git-ssh-upload.txt | 48 ---------------------------
> > Documentation/git-tar-tree.txt | 2 +-
> > Documentation/git-var.txt | 3 +-
>
> Last time I checked, git-var was still alive.
Oops. Please ignore that one.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH v2] user-manual: add advanced topic "bisecting merges"
From: Johannes Schindelin @ 2007-11-08 15:23 UTC (permalink / raw)
To: Benoit Sigoure
Cc: Steffen Prohaska, Johannes Sixt, Andreas Ericsson,
Ralf Wildenhues, Git Mailing List
In-Reply-To: <5498B368-053A-424E-B901-A55B66FF5801@lrde.epita.fr>
Hi,
On Thu, 8 Nov 2007, Benoit Sigoure wrote:
> On Nov 8, 2007, at 1:54 PM, Steffen Prohaska wrote:
>
> > Do you use rebase like this in real life?
> >
> > I thought of the text as background information that might be helpful
> > for users who want do decide wether to merge or to rebase. The problem
> > described may be valuable information supporting a decision about a
> > recommended workflow for a group of users.
>
> You're missing the point. Johannes suggested that you rebase *only* for
> bisecting purpose. Once you find the culprit commit, throw away your
> rebased stuff.
Just to clear things up: it was the other Johannes who suggested it.
But I strongly advise not to rebase before bisecting, since you could very
well end up changing the behaviour of the program by rebasing it. Even to
a point where you cannot bisect it any longer, for example when the merge
of two branches contains an important fix without which the combined
branches (or even parts of them) will not even compile.
Last time I checked, however, bisect worked like a charm even on
a history with complicated ancestry.
Ciao,
Dscho
^ permalink raw reply
* Re: git pull opinion
From: Johannes Schindelin @ 2007-11-08 15:27 UTC (permalink / raw)
To: Aghiles; +Cc: Linus Torvalds, Bill Lear, Junio C Hamano, git
In-Reply-To: <3abd05a90711071325y397434efq7d4e50cb7a1cf07e@mail.gmail.com>
Hi,
On Wed, 7 Nov 2007, Aghiles wrote:
> On 11/6/07, Linus Torvalds <torvalds@linux-foundation.org> wrote:
>
> > Now, I do think that we could relax the rule so that "files that are
> > modified must be clean in the working tree" could instead become
> > "files that actually don't merge _trivially_ must be clean in the
> > working tree". But basically, if it's not a trivial merge, then since
> > it's done in the working tree, the working tree has to be clean (or
> > the merge would overwrite it).
>
> I really think this is a good idea. It seems to me that the first "bad"
> surprise a svn/cvs/bk user will have is the result of a "git pull" command
> on a dirty tree. With the proposed change, and if I understand correctly:
> - users that are used to commit often and fetch into clean trees
> will never be bothered by this change.
> - users that are used to "update" often are expecting to resolve
> conflicts in their working copy anyway.
But the latter ones will likely not understand why all of a sudden their
working tree has to be clean sometimes (when there was no trivial
merge possible).
Besides, I think it is not trivial to implement.
Not my itch,
Dscho
^ permalink raw reply
* Re: [PATCH MISC 1/1] Make gcc warning about empty if body go away.
From: Jon Loeliger @ 2007-11-08 15:20 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: Junio C Hamano, Pierre Habouzit, git
In-Reply-To: <4732CBD3.503@op5.se>
Andreas Ericsson wrote:
>> + if (active_cache_changed &&
>> + !write_cache(fd, active_cache, active_nr) && !close(fd))
>> + commit_locked_index(lock_file);
>> rollback_lock_file(lock_file);
>> }
>>
>
> Ack, obviously, as it no longer requires a comment to explain it, although
> I'd prefer an empty line after commit_locked_index(lock_file); so as to not
> confuse the rollback_lock_file() statement as being part of the conditional
> path.
>
> First I thought the rollback_lock_file() was the *only* statement to the
> condition, and everyone who uses 4 for tabsize) will have double trouble
> since commit_locked_index(lock_file) aligns with the second line of the
> condition.
>
It's too bad this was confusing due to the lack of braces. :-)
jdl
^ permalink raw reply
* Re: [PATCH 4/3] t3700: avoid racy git situation
From: Kristian Høgsberg @ 2007-11-08 15:27 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Johannes Sixt, git, gitster
In-Reply-To: <Pine.LNX.4.64.0711081511440.4362@racer.site>
On Thu, 2007-11-08 at 15:16 +0000, Johannes Schindelin wrote:
> Hi,
>
> On Thu, 8 Nov 2007, Johannes Sixt wrote:
>
> > Johannes Schindelin schrieb:
> > > Wow, the builtin commit is fast. It sometimes triggers a racy
> > > situation in the test case for "git add --refresh -- foo".
> > >
> > > So when that test fails, simply sleep one second and try again.
> >
> > [/me looks at the calender - no, it's not April Fool's day]
>
> No. The builtin commit is really that fast.
>
> > Wouldn't it be better to fix git-commit (or git-add)? I would like to
> > help, but you already seem to have done the analysis, so...
>
> The problem is that the index has the same timestamp as the file "foo".
>
> Therefore, git cannot tell if "foo" is up-to-date in the index, since it
> could have been modified (and indeed is) just a fraction of a second later
> than the index was last updated.
>
> And since diff-index, as called from the test script, does not generate a
> diff, but really only determines if the index information suggests that
> the files are up-to-date, there is not really much you can do.
>
> This is our good old friend, the racy git problem.
>
> NOTE: other scms do not have this problem, mostly because they are too
> slow to trigger it.
Ah, so that's what that was. Thanks for fixing that, I wasn't sure what
was going on and didn't have the time to dig into it. I have a patch
that fixes the author date for commit in determine_author_info() instead
and fixes some of the option validation problems mentioned by Björn. I
have an updated launch_editor patch too, but what you sent out looks
good, so I'll just send the latest builtin-commit.
cheers,
Kristian
^ permalink raw reply
* Re: [PATCH 4/3] t3700: avoid racy git situation
From: Johannes Sixt @ 2007-11-08 15:45 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, krh, gitster
In-Reply-To: <Pine.LNX.4.64.0711081511440.4362@racer.site>
Johannes Schindelin schrieb:
> The problem is that the index has the same timestamp as the file "foo".
>
> Therefore, git cannot tell if "foo" is up-to-date in the index, since it
> could have been modified (and indeed is) just a fraction of a second later
> than the index was last updated.
The test goes like this:
>foo && git add foo && git commit -a -m "commit all" &&
test -z "`git diff-index HEAD -- foo`" &&
git read-tree HEAD &&
case "`git diff-index HEAD -- foo`" in
:100644" "*"M foo") echo ok;;
*) echo fail; (exit 1);;
esac &&
git add --refresh -- foo &&
test -z "`git diff-index HEAD -- foo`"
I'm pretty sure that this test completed within one second even before
git-commit was a builtin. (I assume that timestamps don't have sub-second
resolution.) Why then didn't this racy-git problem occur with the scripted
git-commit?
-- Hannes
^ permalink raw reply
* Re: [PATCH] Allow git-instaweb to be run from bare repositories
From: Jonas Fonseca @ 2007-11-08 15:46 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vodec31k0.fsf@gitster.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> wrote Fri, Nov 02, 2007:
> Jonas Fonseca <fonseca@diku.dk> writes:
>
> > Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
> > ---
> > git-instaweb.sh | 1 +
> > 1 files changed, 1 insertions(+), 0 deletions(-)
> >
> > On IRC yesterday, two were asking for this and it seems simply enough.
> > Apparently, git-instaweb is a great way to see if you've set up a
> > remote repository correctly.
> >
> > diff --git a/git-instaweb.sh b/git-instaweb.sh
> > index 95c3e5a..14917ac 100755
> > --- a/git-instaweb.sh
> > +++ b/git-instaweb.sh
> > @@ -6,6 +6,7 @@ USAGE='[--start] [--stop] [--restart]
> > [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
> > [--module-path=<path> (for Apache2 only)]'
> >
> > +SUBDIRECTORY_OK=Yes
> > . git-sh-setup
> >
> > fqgitdir="$GIT_DIR"
>
> I'd agree with the goal of the patch, and I would not doubt the
> patch worked for you, but it somewhat feels wrong that you can
> say "It is Ok to run this script from a subdirectory" and that
> is the magic to allow something to work in a bare repository.
>
> Care to share your insights on what is going on?
After disecting the source code in more depth as well as the trace of
running git-instaweb inside a bare repository, the command relies on
git-sh-setup (which is assumed to be safe in bare repos) to initialize a
sane value of GIT_DIR. It uses GIT_DIR to initialize:
+ fqgitdir=/tmp/git.git
which is never touched later (and could possibly be changed to just use
the GIT_DIR variable instead, since either is used interchangably
throughout the script. In fact, it later does:
GIT_DIR="$fqgitdir"
which could be removed. Anyway, this fqgitdir variable is used for
creating a place for gitweb and HTTP server configuration files under
"$fqgitdir/gitweb".
+ mkdir -p /tmp/git.git/gitweb/tmp
The remaining things in the script is option handling and setting up the
HTTP server configuration. The only place where I see it possibly
"spilling out" into the parent directory is this sequence:
+ dirname /tmp/git.git
+ script=
s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "/tmp";#
s#\(my\|our\) $gitbin =.*#\1 $gitbin = "/home/fonseca/bin";#
s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
s#\(my\|our\) $git_temp =.*#\1 $git_temp = "/tmp/git.git/gitweb/tmp";#
Where it creates gitweb.cgi. This might make gitweb try to look for .git
directories in subdirectories. Maybe it should create a projects_list
file to explicitly list that only "$barerepo.git" is exported?
--
Jonas Fonseca
^ permalink raw reply
* Re: [PATCH MISC 1/1] Make gcc warning about empty if body go away.
From: Andreas Ericsson @ 2007-11-08 15:47 UTC (permalink / raw)
To: Jon Loeliger; +Cc: Junio C Hamano, Pierre Habouzit, git
In-Reply-To: <47332957.5050308@freescale.com>
Jon Loeliger wrote:
> Andreas Ericsson wrote:
>
>>> + if (active_cache_changed &&
>>> + !write_cache(fd, active_cache, active_nr) && !close(fd))
>>> + commit_locked_index(lock_file);
>>> rollback_lock_file(lock_file);
>>> }
>>>
>>
>> Ack, obviously, as it no longer requires a comment to explain it,
>> although
>> I'd prefer an empty line after commit_locked_index(lock_file); so as
>> to not
>> confuse the rollback_lock_file() statement as being part of the
>> conditional
>> path.
>>
>> First I thought the rollback_lock_file() was the *only* statement to the
>> condition, and everyone who uses 4 for tabsize) will have double trouble
>> since commit_locked_index(lock_file) aligns with the second line of the
>> condition.
>>
>
> It's too bad this was confusing due to the lack of braces. :-)
>
I found that slightly ironic too, since I really don't like overly bracey
code either. Just goes to show there are no rules without exceptions, I
guess.
--
Andreas Ericsson andreas.ericsson@op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
^ permalink raw reply
* [PATCH] instaweb: Minor cleanups and fixes for potential problems
From: Jonas Fonseca @ 2007-11-08 15:49 UTC (permalink / raw)
To: git, Junio C Hamano
Fix path quoting and test of empty values that some shells do not like.
Remove duplicate check and setting of $browser.
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
---
git-instaweb.sh | 11 +++++------
1 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/git-instaweb.sh b/git-instaweb.sh
index f05884c..fbacfb1 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -44,7 +44,7 @@ start_httpd () {
httpd_only="`echo $httpd | cut -f1 -d' '`"
if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
then
- $httpd $fqgitdir/gitweb/httpd.conf
+ $httpd "$fqgitdir/gitweb/httpd.conf"
else
# many httpds are installed in /usr/sbin or /usr/local/sbin
# these days and those are not in most users $PATHs
@@ -158,7 +158,7 @@ EOF
:DirectoryIndex: ["gitweb.cgi"]
:PidFile: "$fqgitdir/pid"
EOF
- test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+ test x"$local" = xtrue && echo ':BindAddress: "127.0.0.1"' >> "$conf"
}
lighttpd_conf () {
@@ -171,14 +171,14 @@ server.pid-file = "$fqgitdir/pid"
cgi.assign = ( ".cgi" => "" )
mimetype.assign = ( ".css" => "text/css" )
EOF
- test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf"
+ test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
}
apache2_conf () {
test -z "$module_path" && module_path=/usr/lib/apache2/modules
mkdir -p "$GIT_DIR/gitweb/logs"
bind=
- test "$local" = true && bind='127.0.0.1:'
+ test x"$local" = xtrue && bind='127.0.0.1:'
echo 'text/css css' > $fqgitdir/mime.types
cat > "$conf" <<EOF
ServerName "git-instaweb"
@@ -271,6 +271,5 @@ webrick)
esac
start_httpd
-test -z "$browser" && browser=echo
url=http://127.0.0.1:$port
-$browser $url || echo $url
+"$browser" $url || echo $url
--
1.5.3.5.1623.g4aab495-dirty
--
Jonas Fonseca
^ permalink raw reply related
* [PATCH] Drop deprecated commands from git(7) and update deprecation notices
From: Jonas Fonseca @ 2007-11-08 16:01 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: Johannes Schindelin, Junio C Hamano, git
In-Reply-To: <4733249B.9020504@op5.se>
Also remove manpages of commands that no longer exist.
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
---
Documentation/cmd-list.perl | 5 ---
Documentation/git-local-fetch.txt | 66 -------------------------------------
Documentation/git-ssh-fetch.txt | 52 -----------------------------
Documentation/git-ssh-upload.txt | 48 ---------------------------
Documentation/git-tar-tree.txt | 2 +-
Documentation/git-var.txt | 5 ++-
6 files changed, 4 insertions(+), 174 deletions(-)
delete mode 100644 Documentation/git-local-fetch.txt
delete mode 100644 Documentation/git-ssh-fetch.txt
delete mode 100644 Documentation/git-ssh-upload.txt
Andreas Ericsson <ae@op5.se> wrote Thu, Nov 08, 2007:
> Jonas Fonseca wrote:
> >... and remove manpages of commands that no longer exists.
> >
> >diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
> >index 8139423..73c37b0 100644
> >--- a/Documentation/git-var.txt
> >+++ b/Documentation/git-var.txt
> >@@ -20,7 +20,8 @@ OPTIONS
> > Cause the logical variables to be listed. In addition, all the
> > variables of the git configuration file .git/config are listed
> > as well. (However, the configuration variables listing functionality
> >- is deprecated in favor of `git-config -l`.)
> >+ is deprecated. Use gitlink:git-config[1] with the option '-l'
> >+ instead.)
> >
>
> Skip the parentheses. It reads better without it.
Yes, you are right. Also updated the commit message to be less
misleading (git-var is not removed but the deprecation notice is
updated).
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 8d21d42..7f1f5d2 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -124,9 +124,7 @@ git-index-pack plumbingmanipulators
git-init mainporcelain
git-instaweb ancillaryinterrogators
gitk mainporcelain
-git-local-fetch synchingrepositories
git-log mainporcelain
-git-lost-found ancillarymanipulators
git-ls-files plumbinginterrogators
git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
@@ -178,8 +176,6 @@ git-show-branch ancillaryinterrogators
git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
git-sh-setup purehelpers
-git-ssh-fetch synchingrepositories
-git-ssh-upload synchingrepositories
git-stash mainporcelain
git-status mainporcelain
git-stripspace purehelpers
@@ -187,7 +183,6 @@ git-submodule mainporcelain
git-svn foreignscminterface
git-symbolic-ref plumbingmanipulators
git-tag mainporcelain
-git-tar-tree plumbinginterrogators
git-unpack-file plumbinginterrogators
git-unpack-objects plumbingmanipulators
git-update-index plumbingmanipulators
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
deleted file mode 100644
index e830dee..0000000
--- a/Documentation/git-local-fetch.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-git-local-fetch(1)
-==================
-
-NAME
-----
-git-local-fetch - Duplicate another git repository on a local system
-
-
-SYNOPSIS
---------
-[verse]
-'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n]
- commit-id path
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Duplicates another git repository on a local system.
-
-OPTIONS
--------
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--s::
- Instead of regular file-to-file copying use symbolic links to the objects
- in the remote repository.
--l::
- Before attempting symlinks (if -s is specified) or file-to-file copying the
- remote objects, try to hardlink the remote objects into the local
- repository.
--n::
- Never attempt to file-to-file copy remote objects. Only useful with
- -s or -l command-line options.
-
--w <filename>::
- Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
- the local end after the transfer is complete.
-
---stdin::
- Instead of a commit id on the command line (which is not expected in this
- case), 'git-local-fetch' expects lines on stdin in the format
-
- <commit-id>['\t'<filename-as-in--w>]
-
---recover::
- Verify that everything reachable from target is fetched. Used after
- an earlier fetch is interrupted.
-
-Author
-------
-Written by Junio C Hamano <junkio@cox.net>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt
deleted file mode 100644
index 8d3e2ff..0000000
--- a/Documentation/git-ssh-fetch.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-git-ssh-fetch(1)
-================
-
-NAME
-----
-git-ssh-fetch - Fetch from a remote repository over ssh connection
-
-
-
-SYNOPSIS
---------
-'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pulls from a remote repository over ssh connection, invoking
-git-ssh-upload on the other end. It functions identically to
-git-ssh-upload, aside from which end you run it on.
-
-
-OPTIONS
--------
-commit-id::
- Either the hash or the filename under [URL]/refs/ to
- pull.
-
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--w::
- Writes the commit-id into the filename under $GIT_DIR/refs/ on
- the local end after the transfer is complete.
-
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt
deleted file mode 100644
index 5e2ca8d..0000000
--- a/Documentation/git-ssh-upload.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-git-ssh-upload(1)
-=================
-
-NAME
-----
-git-ssh-upload - Push to a remote repository over ssh connection
-
-
-SYNOPSIS
---------
-'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pushes from a remote repository over ssh connection, invoking
-git-ssh-fetch on the other end. It functions identically to
-git-ssh-fetch, aside from which end you run it on.
-
-OPTIONS
--------
-commit-id::
- Id of commit to push.
-
--c::
- Get the commit objects.
--t::
- Get tree associated with the requested commit object.
--a::
- Get all the objects.
--v::
- Report what is uploaded.
--w::
- Writes the commit-id into the filename under [URL]/refs/ on
- the remote end after the transfer is complete.
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by Daniel Barkalow
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index 434607b..4aaf813 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -12,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-THIS COMMAND IS DEPRECATED. Use `git-archive` with `--format=tar`
+THIS COMMAND IS DEPRECATED. Use gitlink:git-archive[1] with `--format=tar`
option instead (and move the <base> argument to `--prefix=base/`).
Creates a tar archive containing the tree structure for the named tree.
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 8139423..7e90a95 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -19,8 +19,9 @@ OPTIONS
-l::
Cause the logical variables to be listed. In addition, all the
variables of the git configuration file .git/config are listed
- as well. (However, the configuration variables listing functionality
- is deprecated in favor of `git-config -l`.)
+ as well. However, the configuration variables listing functionality
+ is deprecated. Use gitlink:git-config[1] with the option '-l'
+ instead.
EXAMPLE
--------
--
1.5.3.5.1622.g41d10
--
Jonas Fonseca
^ permalink raw reply related
* Re: [PATCH 4/4] Implement git commit and status as a builtin commands.
From: Kristian Høgsberg @ 2007-11-08 16:01 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0711031328500.4362@racer.site>
On Sat, 2007-11-03 at 13:56 +0000, Johannes Schindelin wrote:
> Hi,
Hi, wanted to send this out earlier, but I punted it and a couple of
days went by...
> On Fri, 2 Nov 2007, Kristian Høgsberg wrote:
>
> > +static char *
> > +prepare_index(const char **files, const char *prefix)
> > +{
> > + int fd;
> > + struct tree *tree;
> > + struct lock_file *next_index_lock;
> > +
> > + fd = hold_locked_index(&lock_file, 1);
> > + if (read_cache() < 0)
> > + die("index file corrupt");
> > +
> > + if (all) {
> > + add_files_to_cache(verbose, NULL, files);
> > + if (write_cache(fd, active_cache, active_nr) || close(fd))
> > + die("unable to write new_index file");
> > + return lock_file.filename;
> > + } else if (also) {
> > + add_files_to_cache(verbose, prefix, files);
> > + if (write_cache(fd, active_cache, active_nr) || close(fd))
> > + die("unable to write new_index file");
> > + return lock_file.filename;
> > + }
>
> Unless something slips by my mind, this could be written as
>
> if (all || also) {
> add_files_to_cache(verbose, also ? prefix : NULL, files);
> ...
> }
Yup, that looks right.
> > +
> > + if (interactive)
> > + interactive_add();
> > +
> > + if (*files == NULL) {
> > + /* Commit index as-is. */
> > + rollback_lock_file(&lock_file);
> > + return get_index_file();
> > + }
>
> Would an interactive add not conflict with this rollback? And indeed with
> the locked index to begin with?
Yes, I've moved the interactive case up to the beginning of
prepare_index() and made it return get_index_file() after running
interactive_add().
> > + /* update the user index file */
> > + add_files_to_cache(verbose, prefix, files);
> > + if (write_cache(fd, active_cache, active_nr) || close(fd))
> > + die("unable to write new_index file");
>
> Does that mean that the index is _always_ written? Even when not
> specifying and paths on the command line?
Do you mean "not specifying any options and paths..."? In that case we
add the files to the index and create a temporary index from HEAD and
add the files there too, which we then commit. So we have to write the
index in that case. If there are no files on the command line, we roll
back the lock and bail out just above the part you quoted.
> > + /* Uh oh, abusing lock_file to create a garbage collected file */
>
> It's not that bad. But I would mention that it is a temporary index which
> you are building.
Heh, yeah, I toned it down a bit :)
> > +static const char sign_off_header[] = "Signed-off-by: ";
>
> Funny, I thought it was a footer ;-)
>
> > + } else if (!stat(template_file, &statbuf)) {
>
> Should this not test "if (template_file && " first?
Yup, added.
> > +/* Find out if the message starting at position 'start' in the strbuf
> > + * contains only whitespace and Signed-off-by lines. */
> > +static int message_is_empty(struct strbuf *sb, int start)
> > +{
> > + struct strbuf tmpl;
> > + const char *nl;
> > + int eol, i;
> > +
> > + /* See if the template is just a prefix of the message. */
> > + strbuf_init(&tmpl, 0);
> > + if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
> > + stripspace(&tmpl, 1);
> > + if (start + tmpl.len <= sb->len &&
> > + memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
> > + start += tmpl.len;
> > + }
> > + strbuf_release(&tmpl);
>
> The release could go inside the if block, no?
Sure, not a big deal, though.
> > +static int run_hook(const char *index_file, const char *name, const char *arg)
>
> Would this function not prefer to live in run-command.c?
Yeah, we can move it later, though.
> > +{
> > + struct child_process hook;
> > + const char *argv[3], *env[2];
> > + char index[PATH_MAX];
> > +
> > + argv[0] = git_path("hooks/%s", name);
> > + argv[1] = arg;
> > + argv[2] = NULL;
> > + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
> > + env[0] = index;
> > + env[1] = NULL;
> > +
> > + if (access(argv[0], X_OK) < 0)
> > + return 0;
>
> That check logically belongs 6 lines higher...
You mean you want to do it right after the argv[0] assignment? I'd like
to keep the block of assignments together so it's easy to see how the
array is set up. Don't tell me you worry about performance here... ;)
> > + rev.abbrev = 0;
> > + rev.diff = 1;
> > + rev.diffopt.output_format =
> > + DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
> > +
> > + rev.verbose_header = 1;
> > + rev.show_root_diff = 1;
> > + rev.commit_format = get_commit_format("format:%h: %s");
>
> That's interesting. Wouldn't have thought of that. Reusing the log_tree
> machinery to output the summary. Cute.
Heh, I just did what the shell script did. It uses git diff-tree for
this, and the above is just the relevant bits from builtin-diff-tree.c.
cheers,
Kristian
^ permalink raw reply
* [PATCH] Port git commit to C.
From: Kristian Høgsberg @ 2007-11-08 16:59 UTC (permalink / raw)
To: gitster; +Cc: git, Kristian Høgsberg
This makes git commit a builtin and moves git-commit.sh to
contrib/examples. This also removes the git-runstatus
helper, which was mostly just a git-status.sh implementation detail.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
This has Johannes' updates and Bjoerns command line parsing fixes.
Also fixes the author date problem on amend commits, so this replaces
Johannes' recent [PATCH 3/3] author fix.
cheers,
Kristian
Makefile | 9 +-
builtin-commit.c | 615 +++++++++++++++++++++++++++++++++++++++
builtin.h | 3 +-
contrib/examples/git-commit.sh | 628 ++++++++++++++++++++++++++++++++++++++++
git-commit.sh | 628 ----------------------------------------
git.c | 3 +-
strbuf.h | 1 +
7 files changed, 1251 insertions(+), 636 deletions(-)
create mode 100644 builtin-commit.c
create mode 100755 contrib/examples/git-commit.sh
delete mode 100755 git-commit.sh
diff --git a/Makefile b/Makefile
index 3ec1876..e4c51c6 100644
--- a/Makefile
+++ b/Makefile
@@ -209,7 +209,7 @@ BASIC_LDFLAGS =
SCRIPT_SH = \
git-bisect.sh git-checkout.sh \
- git-clean.sh git-clone.sh git-commit.sh \
+ git-clean.sh git-clone.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
@@ -256,7 +256,7 @@ EXTRA_PROGRAMS =
BUILT_INS = \
git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
git-get-tar-commit-id$X git-init$X git-repo-config$X \
- git-fsck-objects$X git-cherry-pick$X \
+ git-fsck-objects$X git-cherry-pick$X git-status$X\
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
# what 'all' will build and 'install' will install, in gitexecdir
@@ -326,6 +326,7 @@ BUILTIN_OBJS = \
builtin-check-attr.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
+ builtin-commit.o \
builtin-commit-tree.o \
builtin-count-objects.o \
builtin-describe.o \
@@ -366,7 +367,6 @@ BUILTIN_OBJS = \
builtin-rev-parse.o \
builtin-revert.o \
builtin-rm.o \
- builtin-runstatus.o \
builtin-shortlog.o \
builtin-show-branch.o \
builtin-stripspace.o \
@@ -832,9 +832,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
chmod +x $@+ && \
mv $@+ $@
-git-status: git-commit
- $(QUIET_GEN)cp $< $@+ && mv $@+ $@
-
gitweb/gitweb.cgi: gitweb/gitweb.perl
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
diff --git a/builtin-commit.c b/builtin-commit.c
new file mode 100644
index 0000000..ae4ca4e
--- /dev/null
+++ b/builtin-commit.c
@@ -0,0 +1,615 @@
+/*
+ * Builtin "git commit"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ */
+
+#include "git-compat-util.h"
+
+#include "cache.h"
+#include "cache-tree.h"
+#include "builtin.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "wt-status.h"
+#include "run-command.h"
+#include "refs.h"
+#include "log-tree.h"
+#include "strbuf.h"
+#include "utf8.h"
+#include "parse-options.h"
+
+static const char * const builtin_commit_usage[] = {
+ "git-commit [options] [--] <filepattern>...",
+ NULL
+};
+
+static unsigned char head_sha1[20], merge_head_sha1[20];
+static char *use_message_buffer;
+static const char commit_editmsg[] = "COMMIT_EDITMSG";
+static struct lock_file lock_file;
+
+static char *logfile, *force_author, *message, *template_file;
+static char *edit_message, *use_message;
+static int all, edit_flag, also, interactive, only, amend, signoff;
+static int quiet, verbose, untracked_files, no_verify;
+
+static int no_edit, initial_commit, in_merge;
+const char *only_include_assumed;
+
+static struct option builtin_commit_options[] = {
+ OPT__QUIET(&quiet),
+ OPT__VERBOSE(&verbose),
+ OPT_GROUP("Commit message options"),
+
+ OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
+ OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
+ OPT_STRING('m', "message", &message, "MESSAGE", "specify commit message"),
+ OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+ OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+ OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
+ OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
+ OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+
+ OPT_GROUP("Commit contents options"),
+ OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+ OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+ OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+ OPT_BOOLEAN('o', "only", &only, ""),
+ OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+ OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+ OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
+
+ OPT_END()
+};
+
+static char *
+prepare_index(const char **files, const char *prefix)
+{
+ int fd;
+ struct tree *tree;
+ struct lock_file *next_index_lock;
+
+ if (interactive) {
+ interactive_add();
+ return get_index_file();
+ }
+
+ fd = hold_locked_index(&lock_file, 1);
+ if (read_cache() < 0)
+ die("index file corrupt");
+
+ if (all || also) {
+ add_files_to_cache(verbose, also ? prefix : NULL, files);
+ if (write_cache(fd, active_cache, active_nr) || close(fd))
+ die("unable to write new_index file");
+ return lock_file.filename;
+ }
+
+ if (*files == NULL) {
+ /* Commit index as-is. */
+ rollback_lock_file(&lock_file);
+ return get_index_file();
+ }
+
+ /* update the user index file */
+ add_files_to_cache(verbose, prefix, files);
+ if (write_cache(fd, active_cache, active_nr) || close(fd))
+ die("unable to write new_index file");
+
+ if (!initial_commit) {
+ tree = parse_tree_indirect(head_sha1);
+ if (!tree)
+ die("failed to unpack HEAD tree object");
+ if (read_tree(tree, 0, NULL))
+ die("failed to read HEAD tree object");
+ }
+
+ /* Use a lock file to garbage collect the temporary index file. */
+ next_index_lock = xmalloc(sizeof(*next_index_lock));
+ fd = hold_lock_file_for_update(next_index_lock,
+ git_path("next-index-%d", getpid()), 1);
+ add_files_to_cache(verbose, prefix, files);
+ if (write_cache(fd, active_cache, active_nr) || close(fd))
+ die("unable to write new_index file");
+
+ return next_index_lock->filename;
+}
+
+static int run_status(FILE *fp, const char *index_file)
+{
+ struct wt_status s;
+
+ wt_status_prepare(&s);
+
+ if (amend) {
+ s.amend = 1;
+ s.reference = "HEAD^1";
+ }
+ s.verbose = verbose;
+ s.untracked = untracked_files;
+ s.index_file = index_file;
+ s.fp = fp;
+
+ wt_status_print(&s);
+
+ return s.commitable;
+}
+
+static const char sign_off_header[] = "Signed-off-by: ";
+
+static int prepare_log_message(const char *index_file)
+{
+ struct stat statbuf;
+ int commitable;
+ struct strbuf sb;
+ char *buffer;
+ FILE *fp;
+
+ strbuf_init(&sb, 0);
+ if (message) {
+ strbuf_add(&sb, message, strlen(message));
+ } else if (logfile && !strcmp(logfile, "-")) {
+ if (isatty(0))
+ fprintf(stderr, "(reading log message from standard input)\n");
+ if (strbuf_read(&sb, 0, 0) < 0)
+ die("could not read log from standard input");
+ } else if (logfile) {
+ if (strbuf_read_file(&sb, logfile, 0) < 0)
+ die("could not read log file '%s': %s",
+ logfile, strerror(errno));
+ } else if (use_message) {
+ buffer = strstr(use_message_buffer, "\n\n");
+ if (!buffer || buffer[2] == '\0')
+ die("commit has empty message");
+ strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+ } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
+ if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
+ die("could not read MERGE_MSG: %s", strerror(errno));
+ } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
+ if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
+ die("could not read SQUASH_MSG: %s", strerror(errno));
+ } else if (template_file && !stat(template_file, &statbuf)) {
+ if (strbuf_read_file(&sb, template_file, 0) < 0)
+ die("could not read %s: %s",
+ template_file, strerror(errno));
+ }
+
+ fp = fopen(git_path(commit_editmsg), "w");
+ if (fp == NULL)
+ die("could not open %s\n", git_path(commit_editmsg));
+
+ stripspace(&sb, 0);
+ if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
+ die("could not write commit template: %s\n",
+ strerror(errno));
+
+ if (signoff) {
+ const char *info, *bol;
+
+ info = git_committer_info(1);
+ strbuf_addch(&sb, '\0');
+ bol = strrchr(sb.buf + sb.len - 1, '\n');
+ if (!bol || prefixcmp(bol, sign_off_header))
+ fprintf(fp, "\n");
+ fprintf(fp, "%s%s\n", sign_off_header, git_committer_info(1));
+ }
+
+ strbuf_release(&sb);
+
+ if (in_merge && !no_edit) {
+ fprintf(fp,
+ "#\n"
+ "# It looks like you may be committing a MERGE.\n"
+ "# If this is not correct, please remove the file\n"
+ "# %s\n"
+ "# and try again.\n"
+ "#\n",
+ git_path("MERGE_HEAD"));
+ }
+
+ fprintf(fp,
+ "\n"
+ "# Please enter the commit message for your changes.\n"
+ "# (Comment lines starting with '#' will not be included)\n");
+ if (only_include_assumed)
+ fprintf(fp, "# %s\n", only_include_assumed);
+
+ commitable = run_status(fp, index_file);
+
+ fclose(fp);
+
+ return commitable;
+}
+
+/* Find out if the message starting at position 'start' in the strbuf
+ * contains only whitespace and Signed-off-by lines. */
+static int message_is_empty(struct strbuf *sb, int start)
+{
+ struct strbuf tmpl;
+ const char *nl;
+ int eol, i;
+
+ /* See if the template is just a prefix of the message. */
+ strbuf_init(&tmpl, 0);
+ if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
+ stripspace(&tmpl, 1);
+ if (start + tmpl.len <= sb->len &&
+ memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
+ start += tmpl.len;
+ strbuf_release(&tmpl);
+ }
+
+ /* Check if the rest is just whitespace and Signed-of-by's. */
+ for (i = start; i < sb->len; i++) {
+ nl = memchr(sb->buf + i, '\n', sb->len - i);
+ if (nl)
+ eol = nl - sb->buf;
+ else
+ eol = sb->len;
+
+ if (strlen(sign_off_header) <= eol - i &&
+ !prefixcmp(sb->buf + i, sign_off_header)) {
+ i = eol;
+ continue;
+ }
+ while (i < eol)
+ if (!isspace(sb->buf[i++]))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void determine_author_info(struct strbuf *sb)
+{
+ char *name, *email, *date;
+
+ name = getenv("GIT_AUTHOR_NAME");
+ email = getenv("GIT_AUTHOR_EMAIL");
+ date = getenv("GIT_AUTHOR_DATE");
+
+ if (use_message) {
+ const char *a, *lb, *rb, *eol;
+
+ a = strstr(use_message_buffer, "\nauthor ");
+ if (!a)
+ die("invalid commit: %s\n", use_message);
+
+ lb = strstr(a + 8, " <");
+ rb = strstr(a + 8, "> ");
+ eol = strchr(a + 8, '\n');
+ if (!lb || !rb || !eol)
+ die("invalid commit: %s\n", use_message);
+
+ name = xstrndup(a + 8, lb - (a + 8));
+ email = xstrndup(lb + 2, rb - (lb + 2));
+ date = xstrndup(rb + 2, eol - (rb + 2));
+ }
+
+ if (force_author) {
+ const char *lb = strstr(force_author, " <");
+ const char *rb = strchr(force_author, '>');
+
+ if (!lb || !rb)
+ die("malformed --author parameter\n");
+ name = xstrndup(force_author, lb - force_author);
+ email = xstrndup(lb + 2, rb - (lb + 2));
+ }
+
+ strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
+}
+
+static int parse_and_validate_options(int argc, const char *argv[])
+{
+ int f = 0;
+
+ argc = parse_options(argc, argv, builtin_commit_options,
+ builtin_commit_usage, 0);
+
+ if (logfile || message || use_message)
+ no_edit = 1;
+ if (edit_flag)
+ no_edit = 0;
+
+ if (get_sha1("HEAD", head_sha1))
+ initial_commit = 1;
+
+ if (!get_sha1("MERGE_HEAD", merge_head_sha1))
+ in_merge = 1;
+
+ /* Sanity check options */
+ if (amend && initial_commit)
+ die("You have nothing to amend.");
+ if (amend && in_merge)
+ die("You are in the middle of a merger -- cannot amend.");
+
+ if (use_message)
+ f++;
+ if (edit_message)
+ f++;
+ if (logfile)
+ f++;
+ if (f > 1)
+ die("Only one of -c/-C/-F can be used.");
+ if (message && f > 0)
+ die("Option -m cannot be combined with -c/-C/-F.");
+ if (edit_message)
+ use_message = edit_message;
+ if (amend)
+ use_message = "HEAD";
+ if (use_message) {
+ unsigned char sha1[20];
+ static char utf8[] = "UTF-8";
+ const char *out_enc;
+ char *enc, *end;
+ struct commit *commit;
+
+ if (get_sha1(use_message, sha1))
+ die("could not lookup commit %s", use_message);
+ commit = lookup_commit(sha1);
+ if (!commit || parse_commit(commit))
+ die("could not parse commit %s", use_message);
+
+ enc = strstr(commit->buffer, "\nencoding");
+ if (enc) {
+ end = strchr(enc + 10, '\n');
+ enc = xstrndup(enc + 10, end - (enc + 10));
+ } else {
+ enc = utf8;
+ }
+ out_enc = git_commit_encoding ? git_commit_encoding : utf8;
+
+ if (strcmp(out_enc, enc))
+ use_message_buffer =
+ reencode_string(commit->buffer, out_enc, enc);
+
+ /* If we failed to reencode the buffer, just copy it
+ * byte for byte so the user can try to fix it up.
+ * This also handles the case where input and output
+ * encodings are identical. */
+ if (use_message_buffer == NULL)
+ use_message_buffer = xstrdup(commit->buffer);
+ if (enc != utf8)
+ free(enc);
+ }
+
+ if (!!also + !!only + !!all + !!interactive > 1)
+ die("Only one of --include/--only/--all/--interactive can be used.");
+ if (argc == 0 && (also || (only && !amend)))
+ die("No paths with --include/--only does not make sense.");
+ if (argc == 0 && only && amend)
+ only_include_assumed = "Clever... amending the last one with dirty index.";
+ if (argc > 0 && !also && !only) {
+ only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
+ also = 0;
+ }
+
+ if (all && argc > 0)
+ die("Paths with -a does not make sense.");
+ else if (interactive && argc > 0)
+ die("Paths with --interactive does not make sense.");
+
+ return argc;
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+ const char *index_file;
+ int commitable;
+
+ git_config(git_status_config);
+
+ argc = parse_and_validate_options(argc, argv);
+
+ index_file = prepare_index(argv, prefix);
+
+ commitable = run_status(stdout, index_file);
+
+ rollback_lock_file(&lock_file);
+
+ return commitable ? 0 : 1;
+}
+
+static int run_hook(const char *index_file, const char *name, const char *arg)
+{
+ struct child_process hook;
+ const char *argv[3], *env[2];
+ char index[PATH_MAX];
+
+ argv[0] = git_path("hooks/%s", name);
+ argv[1] = arg;
+ argv[2] = NULL;
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ env[0] = index;
+ env[1] = NULL;
+
+ if (access(argv[0], X_OK) < 0)
+ return 0;
+
+ memset(&hook, 0, sizeof(hook));
+ hook.argv = argv;
+ hook.no_stdin = 1;
+ hook.stdout_to_stderr = 1;
+ hook.env = env;
+
+ return run_command(&hook);
+}
+
+static void print_summary(const char *prefix, const unsigned char *sha1)
+{
+ struct rev_info rev;
+ struct commit *commit;
+
+ commit = lookup_commit(sha1);
+ if (!commit)
+ die("couldn't look up newly created commit\n");
+ if (!commit || parse_commit(commit))
+ die("could not parse newly created commit");
+
+ init_revisions(&rev, prefix);
+ setup_revisions(0, NULL, &rev, NULL);
+
+ rev.abbrev = 0;
+ rev.diff = 1;
+ rev.diffopt.output_format =
+ DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+ rev.verbose_header = 1;
+ rev.show_root_diff = 1;
+ rev.commit_format = get_commit_format("format:%h: %s");
+ rev.always_show_header = 1;
+
+ printf("Created %scommit ", initial_commit ? "initial " : "");
+
+ log_tree_commit(&rev, commit);
+}
+
+int git_commit_config(const char *k, const char *v)
+{
+ if (!strcmp(k, "commit.template")) {
+ template_file = xstrdup(v);
+ return 0;
+ }
+
+ return git_status_config(k, v);
+}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int cmd_commit(int argc, const char **argv, const char *prefix)
+{
+ int header_len, parent_count = 0;
+ struct strbuf sb;
+ const char *index_file, *reflog_msg;
+ char *nl, *header_line;
+ unsigned char commit_sha1[20];
+ struct ref_lock *ref_lock;
+
+ git_config(git_commit_config);
+
+ argc = parse_and_validate_options(argc, argv);
+
+ index_file = prepare_index(argv, prefix);
+
+ if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+ exit(1);
+
+ if (!prepare_log_message(index_file) && !in_merge) {
+ run_status(stdout, index_file);
+ unlink(commit_editmsg);
+ return 1;
+ }
+
+ strbuf_init(&sb, 0);
+
+ /* Start building up the commit header */
+ read_cache_from(index_file);
+ active_cache_tree = cache_tree();
+ if (cache_tree_update(active_cache_tree,
+ active_cache, active_nr, 0, 0) < 0)
+ die("Error building trees");
+ strbuf_addf(&sb, "tree %s\n",
+ sha1_to_hex(active_cache_tree->sha1));
+
+ /* Determine parents */
+ if (initial_commit) {
+ reflog_msg = "commit (initial)";
+ parent_count = 0;
+ } else if (amend) {
+ struct commit_list *c;
+ struct commit *commit;
+
+ reflog_msg = "commit (amend)";
+ commit = lookup_commit(head_sha1);
+ if (!commit || parse_commit(commit))
+ die("could not parse HEAD commit");
+
+ for (c = commit->parents; c; c = c->next)
+ strbuf_addf(&sb, "parent %s\n",
+ sha1_to_hex(c->item->object.sha1));
+ } else if (in_merge) {
+ struct strbuf m;
+ FILE *fp;
+
+ reflog_msg = "commit (merge)";
+ strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+ strbuf_init(&m, 0);
+ fp = fopen(git_path("MERGE_HEAD"), "r");
+ if (fp == NULL)
+ die("could not open %s for reading: %s",
+ git_path("MERGE_HEAD"), strerror(errno));
+ while (strbuf_getline(&m, fp, '\n') != EOF)
+ strbuf_addf(&sb, "parent %s\n", m.buf);
+ fclose(fp);
+ strbuf_release(&m);
+ } else {
+ reflog_msg = "commit";
+ strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+ }
+
+ determine_author_info(&sb);
+ strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
+ if (!is_encoding_utf8(git_commit_encoding))
+ strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
+ strbuf_addch(&sb, '\n');
+
+ /* Get the commit message and validate it */
+ header_len = sb.len;
+ if (!no_edit) {
+ fprintf(stderr, "launching editor, log %s\n", logfile);
+ launch_editor(git_path(commit_editmsg), &sb);
+ }
+ else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0)
+ die("could not read commit message\n");
+ if (run_hook(index_file, "commit-msg", commit_editmsg))
+ exit(1);
+ stripspace(&sb, 1);
+ if (sb.len < header_len ||
+ message_is_empty(&sb, header_len))
+ die("* no commit message? aborting commit.");
+ strbuf_addch(&sb, '\0');
+ if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
+ fprintf(stderr, commit_utf8_warn);
+
+ if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1))
+ die("failed to write commit object");
+
+ ref_lock = lock_any_ref_for_update("HEAD",
+ initial_commit ? NULL : head_sha1,
+ 0);
+
+ nl = strchr(sb.buf + header_len, '\n');
+ header_line = xstrndup(sb.buf + header_len,
+ nl - (sb.buf + header_len));
+ strbuf_release(&sb);
+ strbuf_addf(&sb, "%s: %s\n", reflog_msg, header_line);
+ strbuf_addch(&sb, '\0');
+ free(header_line);
+
+ if (!ref_lock)
+ die("cannot lock HEAD ref");
+ if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0)
+ die("cannot update HEAD ref");
+
+ unlink(git_path("MERGE_HEAD"));
+ unlink(git_path("MERGE_MSG"));
+
+ if (lock_file.filename[0] && commit_locked_index(&lock_file))
+ die("failed to write new index");
+
+ rerere();
+
+ run_hook(index_file, "post-commit", NULL);
+
+ if (!quiet)
+ print_summary(prefix, commit_sha1);
+
+ return 0;
+}
diff --git a/builtin.h b/builtin.h
index 2335c01..3d2de27 100644
--- a/builtin.h
+++ b/builtin.h
@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_commit(int argc, const char **argv, const char *prefix);
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
extern int cmd_describe(int argc, const char **argv, const char *prefix);
@@ -69,11 +70,11 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_revert(int argc, const char **argv, const char *prefix);
extern int cmd_rm(int argc, const char **argv, const char *prefix);
-extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_status(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
extern int cmd_tag(int argc, const char **argv, const char *prefix);
diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh
new file mode 100755
index 0000000..fcb8443
--- /dev/null
+++ b/contrib/examples/git-commit.sh
@@ -0,0 +1,628 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+# Copyright (c) 2006 Junio C Hamano
+
+USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+require_work_tree
+
+git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
+
+case "$0" in
+*status)
+ status_only=t
+ ;;
+*commit)
+ status_only=
+ ;;
+esac
+
+refuse_partial () {
+ echo >&2 "$1"
+ echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
+ exit 1
+}
+
+TMP_INDEX=
+THIS_INDEX="$GIT_DIR/index"
+NEXT_INDEX="$GIT_DIR/next-index$$"
+rm -f "$NEXT_INDEX"
+save_index () {
+ cp -p "$THIS_INDEX" "$NEXT_INDEX"
+}
+
+run_status () {
+ # If TMP_INDEX is defined, that means we are doing
+ # "--only" partial commit, and that index file is used
+ # to build the tree for the commit. Otherwise, if
+ # NEXT_INDEX exists, that is the index file used to
+ # make the commit. Otherwise we are using as-is commit
+ # so the regular index file is what we use to compare.
+ if test '' != "$TMP_INDEX"
+ then
+ GIT_INDEX_FILE="$TMP_INDEX"
+ export GIT_INDEX_FILE
+ elif test -f "$NEXT_INDEX"
+ then
+ GIT_INDEX_FILE="$NEXT_INDEX"
+ export GIT_INDEX_FILE
+ fi
+
+ if test "$status_only" = "t" -o "$use_status_color" = "t"; then
+ color=
+ else
+ color=--nocolor
+ fi
+ git runstatus ${color} \
+ ${verbose:+--verbose} \
+ ${amend:+--amend} \
+ ${untracked_files:+--untracked}
+}
+
+trap '
+ test -z "$TMP_INDEX" || {
+ test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
+ }
+ rm -f "$NEXT_INDEX"
+' 0
+
+################################################################
+# Command line argument parsing and sanity checking
+
+all=
+also=
+interactive=
+only=
+logfile=
+use_commit=
+amend=
+edit_flag=
+no_edit=
+log_given=
+log_message=
+verify=t
+quiet=
+verbose=
+signoff=
+force_author=
+only_include_assumed=
+untracked_files=
+templatefile="`git config commit.template`"
+while test $# != 0
+do
+ case "$1" in
+ -F|--F|-f|--f|--fi|--fil|--file)
+ case "$#" in 1) usage ;; esac
+ shift
+ no_edit=t
+ log_given=t$log_given
+ logfile="$1"
+ ;;
+ -F*|-f*)
+ no_edit=t
+ log_given=t$log_given
+ logfile="${1#-[Ff]}"
+ ;;
+ --F=*|--f=*|--fi=*|--fil=*|--file=*)
+ no_edit=t
+ log_given=t$log_given
+ logfile="${1#*=}"
+ ;;
+ -a|--a|--al|--all)
+ all=t
+ ;;
+ --au=*|--aut=*|--auth=*|--autho=*|--author=*)
+ force_author="${1#*=}"
+ ;;
+ --au|--aut|--auth|--autho|--author)
+ case "$#" in 1) usage ;; esac
+ shift
+ force_author="$1"
+ ;;
+ -e|--e|--ed|--edi|--edit)
+ edit_flag=t
+ ;;
+ -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
+ also=t
+ ;;
+ --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
+ --interactiv|--interactive)
+ interactive=t
+ ;;
+ -o|--o|--on|--onl|--only)
+ only=t
+ ;;
+ -m|--m|--me|--mes|--mess|--messa|--messag|--message)
+ case "$#" in 1) usage ;; esac
+ shift
+ log_given=m$log_given
+ log_message="${log_message:+${log_message}
+
+}$1"
+ no_edit=t
+ ;;
+ -m*)
+ log_given=m$log_given
+ log_message="${log_message:+${log_message}
+
+}${1#-m}"
+ no_edit=t
+ ;;
+ --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
+ log_given=m$log_given
+ log_message="${log_message:+${log_message}
+
+}${1#*=}"
+ no_edit=t
+ ;;
+ -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
+ --no-verify)
+ verify=
+ ;;
+ --a|--am|--ame|--amen|--amend)
+ amend=t
+ use_commit=HEAD
+ ;;
+ -c)
+ case "$#" in 1) usage ;; esac
+ shift
+ log_given=t$log_given
+ use_commit="$1"
+ no_edit=
+ ;;
+ --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
+ --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
+ --reedit-messag=*|--reedit-message=*)
+ log_given=t$log_given
+ use_commit="${1#*=}"
+ no_edit=
+ ;;
+ --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
+ --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
+ --reedit-message)
+ case "$#" in 1) usage ;; esac
+ shift
+ log_given=t$log_given
+ use_commit="$1"
+ no_edit=
+ ;;
+ -C)
+ case "$#" in 1) usage ;; esac
+ shift
+ log_given=t$log_given
+ use_commit="$1"
+ no_edit=t
+ ;;
+ --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
+ --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
+ --reuse-message=*)
+ log_given=t$log_given
+ use_commit="${1#*=}"
+ no_edit=t
+ ;;
+ --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
+ --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
+ case "$#" in 1) usage ;; esac
+ shift
+ log_given=t$log_given
+ use_commit="$1"
+ no_edit=t
+ ;;
+ -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+ signoff=t
+ ;;
+ -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
+ case "$#" in 1) usage ;; esac
+ shift
+ templatefile="$1"
+ no_edit=
+ ;;
+ -q|--q|--qu|--qui|--quie|--quiet)
+ quiet=t
+ ;;
+ -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+ verbose=t
+ ;;
+ -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
+ --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
+ --untracked-file|--untracked-files)
+ untracked_files=t
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+case "$edit_flag" in t) no_edit= ;; esac
+
+################################################################
+# Sanity check options
+
+case "$amend,$initial_commit" in
+t,t)
+ die "You do not have anything to amend." ;;
+t,)
+ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+ die "You are in the middle of a merge -- cannot amend."
+ fi ;;
+esac
+
+case "$log_given" in
+tt*)
+ die "Only one of -c/-C/-F can be used." ;;
+*tm*|*mt*)
+ die "Option -m cannot be combined with -c/-C/-F." ;;
+esac
+
+case "$#,$also,$only,$amend" in
+*,t,t,*)
+ die "Only one of --include/--only can be used." ;;
+0,t,,* | 0,,t,)
+ die "No paths with --include/--only does not make sense." ;;
+0,,t,t)
+ only_include_assumed="# Clever... amending the last one with dirty index." ;;
+0,,,*)
+ ;;
+*,,,*)
+ only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
+ also=
+ ;;
+esac
+unset only
+case "$all,$interactive,$also,$#" in
+*t,*t,*)
+ die "Cannot use -a, --interactive or -i at the same time." ;;
+t,,[1-9]*)
+ die "Paths with -a does not make sense." ;;
+,t,[1-9]*)
+ die "Paths with --interactive does not make sense." ;;
+,,t,0)
+ die "No paths with -i does not make sense." ;;
+esac
+
+if test ! -z "$templatefile" -a -z "$log_given"
+then
+ if test ! -f "$templatefile"
+ then
+ die "Commit template file does not exist."
+ fi
+fi
+
+################################################################
+# Prepare index to have a tree to be committed
+
+case "$all,$also" in
+t,)
+ if test ! -f "$THIS_INDEX"
+ then
+ die 'nothing to commit (use "git add file1 file2" to include for commit)'
+ fi
+ save_index &&
+ (
+ cd_to_toplevel &&
+ GIT_INDEX_FILE="$NEXT_INDEX" &&
+ export GIT_INDEX_FILE &&
+ git diff-files --name-only -z |
+ git update-index --remove -z --stdin
+ ) || exit
+ ;;
+,t)
+ save_index &&
+ git ls-files --error-unmatch -- "$@" >/dev/null || exit
+
+ git diff-files --name-only -z -- "$@" |
+ (
+ cd_to_toplevel &&
+ GIT_INDEX_FILE="$NEXT_INDEX" &&
+ export GIT_INDEX_FILE &&
+ git update-index --remove -z --stdin
+ ) || exit
+ ;;
+,)
+ if test "$interactive" = t; then
+ git add --interactive || exit
+ fi
+ case "$#" in
+ 0)
+ ;; # commit as-is
+ *)
+ if test -f "$GIT_DIR/MERGE_HEAD"
+ then
+ refuse_partial "Cannot do a partial commit during a merge."
+ fi
+
+ TMP_INDEX="$GIT_DIR/tmp-index$$"
+ W=
+ test -z "$initial_commit" && W=--with-tree=HEAD
+ commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
+
+ # Build a temporary index and update the real index
+ # the same way.
+ if test -z "$initial_commit"
+ then
+ GIT_INDEX_FILE="$THIS_INDEX" \
+ git read-tree --index-output="$TMP_INDEX" -i -m HEAD
+ else
+ rm -f "$TMP_INDEX"
+ fi || exit
+
+ printf '%s\n' "$commit_only" |
+ GIT_INDEX_FILE="$TMP_INDEX" \
+ git update-index --add --remove --stdin &&
+
+ save_index &&
+ printf '%s\n' "$commit_only" |
+ (
+ GIT_INDEX_FILE="$NEXT_INDEX"
+ export GIT_INDEX_FILE
+ git update-index --add --remove --stdin
+ ) || exit
+ ;;
+ esac
+ ;;
+esac
+
+################################################################
+# If we do as-is commit, the index file will be THIS_INDEX,
+# otherwise NEXT_INDEX after we make this commit. We leave
+# the index as is if we abort.
+
+if test -f "$NEXT_INDEX"
+then
+ USE_INDEX="$NEXT_INDEX"
+else
+ USE_INDEX="$THIS_INDEX"
+fi
+
+case "$status_only" in
+t)
+ # This will silently fail in a read-only repository, which is
+ # what we want.
+ GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
+ run_status
+ exit $?
+ ;;
+'')
+ GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
+ ;;
+esac
+
+################################################################
+# Grab commit message, write out tree and make commit.
+
+if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
+then
+ GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
+ || exit
+fi
+
+if test "$log_message" != ''
+then
+ printf '%s\n' "$log_message"
+elif test "$logfile" != ""
+then
+ if test "$logfile" = -
+ then
+ test -t 0 &&
+ echo >&2 "(reading log message from standard input)"
+ cat
+ else
+ cat <"$logfile"
+ fi
+elif test "$use_commit" != ""
+then
+ encoding=$(git config i18n.commitencoding || echo UTF-8)
+ git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
+ sed -e '1,/^$/d' -e 's/^ //'
+elif test -f "$GIT_DIR/MERGE_MSG"
+then
+ cat "$GIT_DIR/MERGE_MSG"
+elif test -f "$GIT_DIR/SQUASH_MSG"
+then
+ cat "$GIT_DIR/SQUASH_MSG"
+elif test "$templatefile" != ""
+then
+ cat "$templatefile"
+fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
+
+case "$signoff" in
+t)
+ sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
+ s/>.*/>/
+ s/^/Signed-off-by: /
+ ')
+ blank_before_signoff=
+ tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
+ grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
+'
+ tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
+ grep "$sign"$ >/dev/null ||
+ printf '%s%s\n' "$blank_before_signoff" "$sign" \
+ >>"$GIT_DIR"/COMMIT_EDITMSG
+ ;;
+esac
+
+if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
+ echo "#"
+ echo "# It looks like you may be committing a MERGE."
+ echo "# If this is not correct, please remove the file"
+ printf '%s\n' "# $GIT_DIR/MERGE_HEAD"
+ echo "# and try again"
+ echo "#"
+fi >>"$GIT_DIR"/COMMIT_EDITMSG
+
+# Author
+if test '' != "$use_commit"
+then
+ eval "$(get_author_ident_from_commit "$use_commit")"
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+fi
+if test '' != "$force_author"
+then
+ GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
+ GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
+ test '' != "$GIT_AUTHOR_NAME" &&
+ test '' != "$GIT_AUTHOR_EMAIL" ||
+ die "malformed --author parameter"
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+fi
+
+PARENTS="-p HEAD"
+if test -z "$initial_commit"
+then
+ rloga='commit'
+ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+ rloga='commit (merge)'
+ PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
+ elif test -n "$amend"; then
+ rloga='commit (amend)'
+ PARENTS=$(git cat-file commit HEAD |
+ sed -n -e '/^$/q' -e 's/^parent /-p /p')
+ fi
+ current="$(git rev-parse --verify HEAD)"
+else
+ if [ -z "$(git ls-files)" ]; then
+ echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
+ exit 1
+ fi
+ PARENTS=""
+ rloga='commit (initial)'
+ current=''
+fi
+set_reflog_action "$rloga"
+
+if test -z "$no_edit"
+then
+ {
+ echo ""
+ echo "# Please enter the commit message for your changes."
+ echo "# (Comment lines starting with '#' will not be included)"
+ test -z "$only_include_assumed" || echo "$only_include_assumed"
+ run_status
+ } >>"$GIT_DIR"/COMMIT_EDITMSG
+else
+ # we need to check if there is anything to commit
+ run_status >/dev/null
+fi
+if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
+then
+ rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
+ use_status_color=t
+ run_status
+ exit 1
+fi
+
+case "$no_edit" in
+'')
+ git-var GIT_AUTHOR_IDENT > /dev/null || die
+ git-var GIT_COMMITTER_IDENT > /dev/null || die
+ git_editor "$GIT_DIR/COMMIT_EDITMSG"
+ ;;
+esac
+
+case "$verify" in
+t)
+ if test -x "$GIT_DIR"/hooks/commit-msg
+ then
+ "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
+ fi
+esac
+
+if test -z "$no_edit"
+then
+ sed -e '
+ /^diff --git a\/.*/{
+ s///
+ q
+ }
+ /^#/d
+ ' "$GIT_DIR"/COMMIT_EDITMSG
+else
+ cat "$GIT_DIR"/COMMIT_EDITMSG
+fi |
+git stripspace >"$GIT_DIR"/COMMIT_MSG
+
+# Test whether the commit message has any content we didn't supply.
+have_commitmsg=
+grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
+ git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
+
+# Is the commit message totally empty?
+if test -s "$GIT_DIR"/COMMIT_BAREMSG
+then
+ if test "$templatefile" != ""
+ then
+ # Test whether this is just the unaltered template.
+ if cnt=`sed -e '/^#/d' < "$templatefile" |
+ git stripspace |
+ diff "$GIT_DIR"/COMMIT_BAREMSG - |
+ wc -l` &&
+ test 0 -lt $cnt
+ then
+ have_commitmsg=t
+ fi
+ else
+ # No template, so the content in the commit message must
+ # have come from the user.
+ have_commitmsg=t
+ fi
+fi
+
+rm -f "$GIT_DIR"/COMMIT_BAREMSG
+
+if test "$have_commitmsg" = "t"
+then
+ if test -z "$TMP_INDEX"
+ then
+ tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
+ else
+ tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
+ rm -f "$TMP_INDEX"
+ fi &&
+ commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
+ rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
+ git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
+ rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
+ if test -f "$NEXT_INDEX"
+ then
+ mv "$NEXT_INDEX" "$THIS_INDEX"
+ else
+ : ;# happy
+ fi
+else
+ echo >&2 "* no commit message? aborting commit."
+ false
+fi
+ret="$?"
+rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
+
+cd_to_toplevel
+
+git rerere
+
+if test "$ret" = 0
+then
+ git gc --auto
+ if test -x "$GIT_DIR"/hooks/post-commit
+ then
+ "$GIT_DIR"/hooks/post-commit
+ fi
+ if test -z "$quiet"
+ then
+ commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
+ --summary --root HEAD --`
+ echo "Created${initial_commit:+ initial} commit $commit"
+ fi
+fi
+
+exit "$ret"
diff --git a/git-commit.sh b/git-commit.sh
deleted file mode 100755
index fcb8443..0000000
--- a/git-commit.sh
+++ /dev/null
@@ -1,628 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-# Copyright (c) 2006 Junio C Hamano
-
-USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-require_work_tree
-
-git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
-
-case "$0" in
-*status)
- status_only=t
- ;;
-*commit)
- status_only=
- ;;
-esac
-
-refuse_partial () {
- echo >&2 "$1"
- echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
- exit 1
-}
-
-TMP_INDEX=
-THIS_INDEX="$GIT_DIR/index"
-NEXT_INDEX="$GIT_DIR/next-index$$"
-rm -f "$NEXT_INDEX"
-save_index () {
- cp -p "$THIS_INDEX" "$NEXT_INDEX"
-}
-
-run_status () {
- # If TMP_INDEX is defined, that means we are doing
- # "--only" partial commit, and that index file is used
- # to build the tree for the commit. Otherwise, if
- # NEXT_INDEX exists, that is the index file used to
- # make the commit. Otherwise we are using as-is commit
- # so the regular index file is what we use to compare.
- if test '' != "$TMP_INDEX"
- then
- GIT_INDEX_FILE="$TMP_INDEX"
- export GIT_INDEX_FILE
- elif test -f "$NEXT_INDEX"
- then
- GIT_INDEX_FILE="$NEXT_INDEX"
- export GIT_INDEX_FILE
- fi
-
- if test "$status_only" = "t" -o "$use_status_color" = "t"; then
- color=
- else
- color=--nocolor
- fi
- git runstatus ${color} \
- ${verbose:+--verbose} \
- ${amend:+--amend} \
- ${untracked_files:+--untracked}
-}
-
-trap '
- test -z "$TMP_INDEX" || {
- test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
- }
- rm -f "$NEXT_INDEX"
-' 0
-
-################################################################
-# Command line argument parsing and sanity checking
-
-all=
-also=
-interactive=
-only=
-logfile=
-use_commit=
-amend=
-edit_flag=
-no_edit=
-log_given=
-log_message=
-verify=t
-quiet=
-verbose=
-signoff=
-force_author=
-only_include_assumed=
-untracked_files=
-templatefile="`git config commit.template`"
-while test $# != 0
-do
- case "$1" in
- -F|--F|-f|--f|--fi|--fil|--file)
- case "$#" in 1) usage ;; esac
- shift
- no_edit=t
- log_given=t$log_given
- logfile="$1"
- ;;
- -F*|-f*)
- no_edit=t
- log_given=t$log_given
- logfile="${1#-[Ff]}"
- ;;
- --F=*|--f=*|--fi=*|--fil=*|--file=*)
- no_edit=t
- log_given=t$log_given
- logfile="${1#*=}"
- ;;
- -a|--a|--al|--all)
- all=t
- ;;
- --au=*|--aut=*|--auth=*|--autho=*|--author=*)
- force_author="${1#*=}"
- ;;
- --au|--aut|--auth|--autho|--author)
- case "$#" in 1) usage ;; esac
- shift
- force_author="$1"
- ;;
- -e|--e|--ed|--edi|--edit)
- edit_flag=t
- ;;
- -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
- also=t
- ;;
- --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
- --interactiv|--interactive)
- interactive=t
- ;;
- -o|--o|--on|--onl|--only)
- only=t
- ;;
- -m|--m|--me|--mes|--mess|--messa|--messag|--message)
- case "$#" in 1) usage ;; esac
- shift
- log_given=m$log_given
- log_message="${log_message:+${log_message}
-
-}$1"
- no_edit=t
- ;;
- -m*)
- log_given=m$log_given
- log_message="${log_message:+${log_message}
-
-}${1#-m}"
- no_edit=t
- ;;
- --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
- log_given=m$log_given
- log_message="${log_message:+${log_message}
-
-}${1#*=}"
- no_edit=t
- ;;
- -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
- --no-verify)
- verify=
- ;;
- --a|--am|--ame|--amen|--amend)
- amend=t
- use_commit=HEAD
- ;;
- -c)
- case "$#" in 1) usage ;; esac
- shift
- log_given=t$log_given
- use_commit="$1"
- no_edit=
- ;;
- --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
- --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
- --reedit-messag=*|--reedit-message=*)
- log_given=t$log_given
- use_commit="${1#*=}"
- no_edit=
- ;;
- --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
- --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
- --reedit-message)
- case "$#" in 1) usage ;; esac
- shift
- log_given=t$log_given
- use_commit="$1"
- no_edit=
- ;;
- -C)
- case "$#" in 1) usage ;; esac
- shift
- log_given=t$log_given
- use_commit="$1"
- no_edit=t
- ;;
- --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
- --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
- --reuse-message=*)
- log_given=t$log_given
- use_commit="${1#*=}"
- no_edit=t
- ;;
- --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
- --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
- case "$#" in 1) usage ;; esac
- shift
- log_given=t$log_given
- use_commit="$1"
- no_edit=t
- ;;
- -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
- signoff=t
- ;;
- -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
- case "$#" in 1) usage ;; esac
- shift
- templatefile="$1"
- no_edit=
- ;;
- -q|--q|--qu|--qui|--quie|--quiet)
- quiet=t
- ;;
- -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
- verbose=t
- ;;
- -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
- --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
- --untracked-file|--untracked-files)
- untracked_files=t
- ;;
- --)
- shift
- break
- ;;
- -*)
- usage
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-case "$edit_flag" in t) no_edit= ;; esac
-
-################################################################
-# Sanity check options
-
-case "$amend,$initial_commit" in
-t,t)
- die "You do not have anything to amend." ;;
-t,)
- if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
- die "You are in the middle of a merge -- cannot amend."
- fi ;;
-esac
-
-case "$log_given" in
-tt*)
- die "Only one of -c/-C/-F can be used." ;;
-*tm*|*mt*)
- die "Option -m cannot be combined with -c/-C/-F." ;;
-esac
-
-case "$#,$also,$only,$amend" in
-*,t,t,*)
- die "Only one of --include/--only can be used." ;;
-0,t,,* | 0,,t,)
- die "No paths with --include/--only does not make sense." ;;
-0,,t,t)
- only_include_assumed="# Clever... amending the last one with dirty index." ;;
-0,,,*)
- ;;
-*,,,*)
- only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
- also=
- ;;
-esac
-unset only
-case "$all,$interactive,$also,$#" in
-*t,*t,*)
- die "Cannot use -a, --interactive or -i at the same time." ;;
-t,,[1-9]*)
- die "Paths with -a does not make sense." ;;
-,t,[1-9]*)
- die "Paths with --interactive does not make sense." ;;
-,,t,0)
- die "No paths with -i does not make sense." ;;
-esac
-
-if test ! -z "$templatefile" -a -z "$log_given"
-then
- if test ! -f "$templatefile"
- then
- die "Commit template file does not exist."
- fi
-fi
-
-################################################################
-# Prepare index to have a tree to be committed
-
-case "$all,$also" in
-t,)
- if test ! -f "$THIS_INDEX"
- then
- die 'nothing to commit (use "git add file1 file2" to include for commit)'
- fi
- save_index &&
- (
- cd_to_toplevel &&
- GIT_INDEX_FILE="$NEXT_INDEX" &&
- export GIT_INDEX_FILE &&
- git diff-files --name-only -z |
- git update-index --remove -z --stdin
- ) || exit
- ;;
-,t)
- save_index &&
- git ls-files --error-unmatch -- "$@" >/dev/null || exit
-
- git diff-files --name-only -z -- "$@" |
- (
- cd_to_toplevel &&
- GIT_INDEX_FILE="$NEXT_INDEX" &&
- export GIT_INDEX_FILE &&
- git update-index --remove -z --stdin
- ) || exit
- ;;
-,)
- if test "$interactive" = t; then
- git add --interactive || exit
- fi
- case "$#" in
- 0)
- ;; # commit as-is
- *)
- if test -f "$GIT_DIR/MERGE_HEAD"
- then
- refuse_partial "Cannot do a partial commit during a merge."
- fi
-
- TMP_INDEX="$GIT_DIR/tmp-index$$"
- W=
- test -z "$initial_commit" && W=--with-tree=HEAD
- commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
-
- # Build a temporary index and update the real index
- # the same way.
- if test -z "$initial_commit"
- then
- GIT_INDEX_FILE="$THIS_INDEX" \
- git read-tree --index-output="$TMP_INDEX" -i -m HEAD
- else
- rm -f "$TMP_INDEX"
- fi || exit
-
- printf '%s\n' "$commit_only" |
- GIT_INDEX_FILE="$TMP_INDEX" \
- git update-index --add --remove --stdin &&
-
- save_index &&
- printf '%s\n' "$commit_only" |
- (
- GIT_INDEX_FILE="$NEXT_INDEX"
- export GIT_INDEX_FILE
- git update-index --add --remove --stdin
- ) || exit
- ;;
- esac
- ;;
-esac
-
-################################################################
-# If we do as-is commit, the index file will be THIS_INDEX,
-# otherwise NEXT_INDEX after we make this commit. We leave
-# the index as is if we abort.
-
-if test -f "$NEXT_INDEX"
-then
- USE_INDEX="$NEXT_INDEX"
-else
- USE_INDEX="$THIS_INDEX"
-fi
-
-case "$status_only" in
-t)
- # This will silently fail in a read-only repository, which is
- # what we want.
- GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
- run_status
- exit $?
- ;;
-'')
- GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
- ;;
-esac
-
-################################################################
-# Grab commit message, write out tree and make commit.
-
-if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
-then
- GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
- || exit
-fi
-
-if test "$log_message" != ''
-then
- printf '%s\n' "$log_message"
-elif test "$logfile" != ""
-then
- if test "$logfile" = -
- then
- test -t 0 &&
- echo >&2 "(reading log message from standard input)"
- cat
- else
- cat <"$logfile"
- fi
-elif test "$use_commit" != ""
-then
- encoding=$(git config i18n.commitencoding || echo UTF-8)
- git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
- sed -e '1,/^$/d' -e 's/^ //'
-elif test -f "$GIT_DIR/MERGE_MSG"
-then
- cat "$GIT_DIR/MERGE_MSG"
-elif test -f "$GIT_DIR/SQUASH_MSG"
-then
- cat "$GIT_DIR/SQUASH_MSG"
-elif test "$templatefile" != ""
-then
- cat "$templatefile"
-fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
-
-case "$signoff" in
-t)
- sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
- s/>.*/>/
- s/^/Signed-off-by: /
- ')
- blank_before_signoff=
- tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
- grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
-'
- tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
- grep "$sign"$ >/dev/null ||
- printf '%s%s\n' "$blank_before_signoff" "$sign" \
- >>"$GIT_DIR"/COMMIT_EDITMSG
- ;;
-esac
-
-if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
- echo "#"
- echo "# It looks like you may be committing a MERGE."
- echo "# If this is not correct, please remove the file"
- printf '%s\n' "# $GIT_DIR/MERGE_HEAD"
- echo "# and try again"
- echo "#"
-fi >>"$GIT_DIR"/COMMIT_EDITMSG
-
-# Author
-if test '' != "$use_commit"
-then
- eval "$(get_author_ident_from_commit "$use_commit")"
- export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
-fi
-if test '' != "$force_author"
-then
- GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
- GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
- test '' != "$GIT_AUTHOR_NAME" &&
- test '' != "$GIT_AUTHOR_EMAIL" ||
- die "malformed --author parameter"
- export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-fi
-
-PARENTS="-p HEAD"
-if test -z "$initial_commit"
-then
- rloga='commit'
- if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
- rloga='commit (merge)'
- PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
- elif test -n "$amend"; then
- rloga='commit (amend)'
- PARENTS=$(git cat-file commit HEAD |
- sed -n -e '/^$/q' -e 's/^parent /-p /p')
- fi
- current="$(git rev-parse --verify HEAD)"
-else
- if [ -z "$(git ls-files)" ]; then
- echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
- exit 1
- fi
- PARENTS=""
- rloga='commit (initial)'
- current=''
-fi
-set_reflog_action "$rloga"
-
-if test -z "$no_edit"
-then
- {
- echo ""
- echo "# Please enter the commit message for your changes."
- echo "# (Comment lines starting with '#' will not be included)"
- test -z "$only_include_assumed" || echo "$only_include_assumed"
- run_status
- } >>"$GIT_DIR"/COMMIT_EDITMSG
-else
- # we need to check if there is anything to commit
- run_status >/dev/null
-fi
-if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
-then
- rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
- use_status_color=t
- run_status
- exit 1
-fi
-
-case "$no_edit" in
-'')
- git-var GIT_AUTHOR_IDENT > /dev/null || die
- git-var GIT_COMMITTER_IDENT > /dev/null || die
- git_editor "$GIT_DIR/COMMIT_EDITMSG"
- ;;
-esac
-
-case "$verify" in
-t)
- if test -x "$GIT_DIR"/hooks/commit-msg
- then
- "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
- fi
-esac
-
-if test -z "$no_edit"
-then
- sed -e '
- /^diff --git a\/.*/{
- s///
- q
- }
- /^#/d
- ' "$GIT_DIR"/COMMIT_EDITMSG
-else
- cat "$GIT_DIR"/COMMIT_EDITMSG
-fi |
-git stripspace >"$GIT_DIR"/COMMIT_MSG
-
-# Test whether the commit message has any content we didn't supply.
-have_commitmsg=
-grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
- git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
-
-# Is the commit message totally empty?
-if test -s "$GIT_DIR"/COMMIT_BAREMSG
-then
- if test "$templatefile" != ""
- then
- # Test whether this is just the unaltered template.
- if cnt=`sed -e '/^#/d' < "$templatefile" |
- git stripspace |
- diff "$GIT_DIR"/COMMIT_BAREMSG - |
- wc -l` &&
- test 0 -lt $cnt
- then
- have_commitmsg=t
- fi
- else
- # No template, so the content in the commit message must
- # have come from the user.
- have_commitmsg=t
- fi
-fi
-
-rm -f "$GIT_DIR"/COMMIT_BAREMSG
-
-if test "$have_commitmsg" = "t"
-then
- if test -z "$TMP_INDEX"
- then
- tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
- else
- tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
- rm -f "$TMP_INDEX"
- fi &&
- commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
- rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
- git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
- rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
- if test -f "$NEXT_INDEX"
- then
- mv "$NEXT_INDEX" "$THIS_INDEX"
- else
- : ;# happy
- fi
-else
- echo >&2 "* no commit message? aborting commit."
- false
-fi
-ret="$?"
-rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
-
-cd_to_toplevel
-
-git rerere
-
-if test "$ret" = 0
-then
- git gc --auto
- if test -x "$GIT_DIR"/hooks/post-commit
- then
- "$GIT_DIR"/hooks/post-commit
- fi
- if test -z "$quiet"
- then
- commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
- --summary --root HEAD --`
- echo "Created${initial_commit:+ initial} commit $commit"
- fi
-fi
-
-exit "$ret"
diff --git a/git.c b/git.c
index 19a2172..1016e04 100644
--- a/git.c
+++ b/git.c
@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
{ "cherry", cmd_cherry, RUN_SETUP },
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+ { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "config", cmd_config },
{ "count-objects", cmd_count_objects, RUN_SETUP },
@@ -347,11 +348,11 @@ static void handle_internal_command(int argc, const char **argv)
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
{ "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
- { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
{ "send-pack", cmd_send_pack, RUN_SETUP },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
+ { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP },
diff --git a/strbuf.h b/strbuf.h
index 9b9e861..9720826 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -113,5 +113,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_getline(struct strbuf *, FILE *, int);
extern void stripspace(struct strbuf *buf, int skip_comments);
+extern void launch_editor(const char *path, struct strbuf *buffer);
#endif /* STRBUF_H */
--
1.5.3.4
^ permalink raw reply related
* [PATCH] SubmittingPatches: improve the 'Patch:' section of the checklist
From: Sergei Organov @ 2007-11-08 16:40 UTC (permalink / raw)
To: git; +Cc: gitster
There were 2 items "send patch to..." but having different set of
addresses to send patch to. Merge them together and move the resulting
item to the end of checklist.
Signed-off-by: Sergei Organov <osv@javad.com>
---
Documentation/SubmittingPatches | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 61635bf..83bf54c 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -20,9 +20,6 @@ Checklist (and a short version for the impatient):
Patch:
- use "git format-patch -M" to create the patch
- - send your patch to <git@vger.kernel.org>. If you use
- git-send-email(1), please test it first by sending
- email to yourself.
- do not PGP sign your patch
- do not attach your patch, but read in the mail
body, unless you cannot teach your mailer to
@@ -31,13 +28,15 @@ Checklist (and a short version for the impatient):
corrupt whitespaces.
- provide additional information (which is unsuitable for
the commit message) between the "---" and the diffstat
- - send the patch to the list (git@vger.kernel.org) and the
- maintainer (gitster@pobox.com).
- if you change, add, or remove a command line option or
make some other user interface change, the associated
documentation should be updated as well.
- if your name is not writable in ASCII, make sure that
you send off a message in the correct encoding.
+ - send the patch to the list (git@vger.kernel.org) and the
+ maintainer (gitster@pobox.com). If you use
+ git-send-email(1), please test it first by sending
+ email to yourself.
Long version:
--
1.5.3.5.529.ge3d6d
^ permalink raw reply related
* git push mirror mode V3
From: Andy Whitcroft @ 2007-11-08 16:58 UTC (permalink / raw)
To: git
In-Reply-To: <20071108121136.GG9736@shadowen.org>
Ok, here is an update based on feedback from the list. I bit the bullet
and added some basic tests. The stack passes the test suite. Hopefully
the attribution is ok on the first patch now. I have kept my
modifications to it a separate patch for the time being to keep
attribution simple.
-apw
^ permalink raw reply
* [PATCH 1/4] mirror pushing
From: Andy Whitcroft @ 2007-11-08 17:00 UTC (permalink / raw)
To: git
In-Reply-To: <20071108165801.GM9736@shadowen.org>
From: Junio C Hamano <gitster@pobox.com>
Existing "git push --all" is almost perfect for backing up to
another repository, except that "--all" only means "all
branches" in modern git, and it does not delete old branches and
tags that exist at the back-up repository that you have removed
from your local repository.
This teaches "git-send-pack" a new "--mirror" option. The
difference from the "--all" option are that (1) it sends all
refs, not just branches, and (2) it deletes old refs you no
longer have on the local side from the remote side.
[apw@shadowen.org: rebase to next post arguments update]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Andy Whitcroft <apw@shadowen.org>
---
builtin-send-pack.c | 40 ++++++++++++++++++++++++++++------------
remote.c | 15 ++++++++++-----
send-pack.h | 1 +
3 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 5a0f5c6..d5ead97 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -8,7 +8,7 @@
#include "send-pack.h"
static const char send_pack_usage[] =
-"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
" --all and explicit <ref> specification are mutually exclusive.";
static struct send_pack_args args = {
@@ -242,7 +242,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
if (!remote_tail)
remote_tail = &remote_refs;
if (match_refs(local_refs, remote_refs, &remote_tail,
- nr_refspec, refspec, args.send_all))
+ nr_refspec, refspec, args.send_all | (args.send_mirror << 1)))
return -1;
if (!remote_refs) {
@@ -259,20 +259,28 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
char old_hex[60], *new_hex;
int will_delete_ref;
const char *pretty_ref;
- const char *pretty_peer;
+ const char *pretty_peer = NULL; /* only used when not deleting */
+ const unsigned char *new_sha1;
- if (!ref->peer_ref)
- continue;
+ if (!ref->peer_ref) {
+ if (!args.send_mirror)
+ continue;
+ new_sha1 = null_sha1;
+ }
+ else
+ new_sha1 = ref->peer_ref->new_sha1;
if (!shown_dest) {
fprintf(stderr, "To %s\n", dest);
shown_dest = 1;
}
+ will_delete_ref = is_null_sha1(new_sha1);
+
pretty_ref = prettify_ref(ref->name);
- pretty_peer = prettify_ref(ref->peer_ref->name);
+ if (!will_delete_ref)
+ pretty_peer = prettify_ref(ref->peer_ref->name);
- will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
if (will_delete_ref && !allow_deleting_refs) {
fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n",
SUMMARY_WIDTH, "[rejected]", pretty_ref);
@@ -280,7 +288,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
continue;
}
if (!will_delete_ref &&
- !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+ !hashcmp(ref->old_sha1, new_sha1)) {
if (args.verbose)
fprintf(stderr, " = %-*s %s -> %s\n",
SUMMARY_WIDTH, "[up to date]",
@@ -312,8 +320,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
!is_null_sha1(ref->old_sha1) &&
!ref->force) {
if (!has_sha1_file(ref->old_sha1) ||
- !ref_newer(ref->peer_ref->new_sha1,
- ref->old_sha1)) {
+ !ref_newer(new_sha1, ref->old_sha1)) {
/* We do not have the remote ref, or
* we know that the remote ref is not
* an ancestor of what we are trying to
@@ -328,7 +335,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
continue;
}
}
- hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+ hashcpy(ref->new_sha1, new_sha1);
if (!will_delete_ref)
new_refs++;
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
@@ -459,6 +466,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.dry_run = 1;
continue;
}
+ if (!strcmp(arg, "--mirror")) {
+ args.send_mirror = 1;
+ continue;
+ }
if (!strcmp(arg, "--force")) {
args.force_update = 1;
continue;
@@ -483,7 +494,12 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
}
if (!dest)
usage(send_pack_usage);
- if (heads && args.send_all)
+ /*
+ * --all and --mirror are incompatible; neither makes sense
+ * with any refspecs.
+ */
+ if ((heads && (args.send_all || args.send_mirror)) ||
+ (args.send_all && args.send_mirror))
usage(send_pack_usage);
if (remote_name) {
diff --git a/remote.c b/remote.c
index 59defdb..45dd59b 100644
--- a/remote.c
+++ b/remote.c
@@ -722,10 +722,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
* without thinking.
*/
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
- int nr_refspec, const char **refspec, int all)
+ int nr_refspec, const char **refspec, int flags)
{
struct refspec *rs =
parse_ref_spec(nr_refspec, (const char **) refspec);
+ int send_all = flags & 01;
+ int send_mirror = flags & 02;
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
return -1;
@@ -742,7 +744,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
if (!pat)
continue;
}
- else if (prefixcmp(src->name, "refs/heads/"))
+ else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
/*
* "matching refs"; traditionally we pushed everything
* including refs outside refs/heads/ hierarchy, but
@@ -763,10 +765,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
if (dst_peer && dst_peer->peer_ref)
/* We're already sending something to this ref. */
goto free_name;
- if (!dst_peer && !nr_refspec && !all)
- /* Remote doesn't have it, and we have no
+
+ if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
+ /*
+ * Remote doesn't have it, and we have no
* explicit pattern, and we don't have
- * --all. */
+ * --all nor --mirror.
+ */
goto free_name;
if (!dst_peer) {
/* Create a new one and link it */
diff --git a/send-pack.h b/send-pack.h
index 7a24f71..8ff1dc3 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -5,6 +5,7 @@ struct send_pack_args {
const char *receivepack;
unsigned verbose:1,
send_all:1,
+ send_mirror:1,
force_update:1,
use_thin_pack:1,
dry_run:1;
^ permalink raw reply related
* [PATCH 2/4] mirror pushing -- clean up match_refs flags
From: Andy Whitcroft @ 2007-11-08 17:01 UTC (permalink / raw)
To: git
In-Reply-To: <20071108165801.GM9736@shadowen.org>
Add a new enum to define the match_refs flags field and switch
all callers to it.
Signed-off-by: Andy Whitcroft <apw@shadowen.org>
---
builtin-send-pack.c | 8 +++++++-
http-push.c | 4 ++--
remote.c | 4 ++--
remote.h | 7 +++++++
4 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index d5ead97..d42164e 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -227,6 +227,12 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
int allow_deleting_refs = 0;
int expect_status_report = 0;
int shown_dest = 0;
+ int flags = MATCH_REFS_NONE;
+
+ if (args.send_all)
+ flags |= MATCH_REFS_ALL;
+ if (args.send_mirror)
+ flags |= MATCH_REFS_MIRROR;
/* No funny business with the matcher */
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
@@ -242,7 +248,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
if (!remote_tail)
remote_tail = &remote_refs;
if (match_refs(local_refs, remote_refs, &remote_tail,
- nr_refspec, refspec, args.send_all | (args.send_mirror << 1)))
+ nr_refspec, refspec, flags))
return -1;
if (!remote_refs) {
diff --git a/http-push.c b/http-push.c
index 99328f5..66b81f1 100644
--- a/http-push.c
+++ b/http-push.c
@@ -78,7 +78,7 @@ static struct curl_slist *no_pragma_header;
static struct curl_slist *default_headers;
static int push_verbosely;
-static int push_all;
+static int push_all = MATCH_REFS_NONE;
static int force_all;
static int dry_run;
@@ -2300,7 +2300,7 @@ int main(int argc, char **argv)
if (*arg == '-') {
if (!strcmp(arg, "--all")) {
- push_all = 1;
+ push_all = MATCH_REFS_ALL;
continue;
}
if (!strcmp(arg, "--force")) {
diff --git a/remote.c b/remote.c
index 45dd59b..09b7aad 100644
--- a/remote.c
+++ b/remote.c
@@ -726,8 +726,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
{
struct refspec *rs =
parse_ref_spec(nr_refspec, (const char **) refspec);
- int send_all = flags & 01;
- int send_mirror = flags & 02;
+ int send_all = flags & MATCH_REFS_ALL;
+ int send_mirror = flags & MATCH_REFS_MIRROR;
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
return -1;
diff --git a/remote.h b/remote.h
index 6a4c7a0..b10036c 100644
--- a/remote.h
+++ b/remote.h
@@ -102,4 +102,11 @@ struct branch *branch_get(const char *name);
int branch_has_merge_config(struct branch *branch);
int branch_merge_matches(struct branch *, int n, const char *);
+/* Flags to match_refs. */
+enum match_refs_flags {
+ MATCH_REFS_NONE = 0,
+ MATCH_REFS_ALL = (1 << 0),
+ MATCH_REFS_MIRROR = (1 << 1),
+};
+
#endif
^ 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