* Re: [PATCH] git daemon: avoid calling syslog() from a signal handler
From: Johannes Schindelin @ 2008-07-05 10:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Brian Foster, git
In-Reply-To: <7vej68u6mr.fsf@gitster.siamese.dyndns.org>
Hi,
On Sat, 5 Jul 2008, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > Signal handlers should never call syslog(), as that can raise signals
> > of its own.
> >
> > Instead, call the syslog() from the master process.
>
> Earlier parts seem to make sense but I am puzzled by these changes.
>
> > @@ -929,7 +945,8 @@ static int service_loop(int socknum, int *socklist)
> > for (;;) {
> > int i;
> >
> > - if (poll(pfd, socknum, -1) < 0) {
> > + i = poll(pfd, socknum, 1);
> > + if (i < 0) {
> > if (errno != EINTR) {
> > error("poll failed, resuming: %s",
> > strerror(errno));
> > @@ -937,6 +954,10 @@ static int service_loop(int socknum, int *socklist)
> > }
> > continue;
> > }
> > + if (i == 0) {
> > + check_dead_children();
> > + continue;
> > + }
>
> So you will check every 1ms to see if there are new dead children, but why
> is this necessary?
This comes from me not reading the man page for poll() properly. Of
course, I want to check every second: syslog timestamps the messages with
a resolution of 1 second, AFAIR, or at least some of them do.
So if you could just squash in this patch, that would be smashing:
-- snipsnap --
@@ -945,8 +945,8 @@ static int service_loop(int socknum, int *socklist)
for (;;) {
int i;
- i = poll(pfd, socknum, 1);
+ i = poll(pfd, socknum, 1000);
if (i < 0) {
if (errno != EINTR) {
error("poll failed, resuming: %s",
strerror(errno));
^ permalink raw reply
* Re: git sequencer prototype
From: Thomas Adam @ 2008-07-05 10:12 UTC (permalink / raw)
To: Alex Riesen; +Cc: Junio C Hamano, Stephan Beyer, git, Johannes Schindelin
In-Reply-To: <20080705081321.GA4935@blimp.local>
2008/7/5 Alex Riesen <fork0@users.sourceforge.net>:
> BTW, what does "am" (git am) mean?
I always thought of it as "apply mbox".
-- Thomas Adam
^ permalink raw reply
* Re: git sequencer prototype
From: Johannes Schindelin @ 2008-07-05 10:13 UTC (permalink / raw)
To: Alex Riesen; +Cc: Junio C Hamano, Stephan Beyer, git
In-Reply-To: <20080705081321.GA4935@blimp.local>
Hi,
On Sat, 5 Jul 2008, Alex Riesen wrote:
> Junio C Hamano, Sat, Jul 05, 2008 00:09:41 +0200:
> > Alex Riesen <raa.lkml@gmail.com> writes:
> >
> > > Stephan Beyer, Tue, Jul 01, 2008 04:38:30 +0200:
> > >>
> > >> here is the patchset for the git-sequencer prototype,
> > >> documentation, test suite and a first git-am and git-rebase-i
> > >> migration. Indeed, monster patches. ;)
> > >
> > > BTW, how about renaming it in something short: git seq. There is
> > > already a seq(1) in GNU coreutils, which does roughly the same
> > > (prints a sequence of numbers), why not reuse the name?
> >
> > Is it advantageous to use shorter but less descriptive name for this
> > command? It will be a backend to am/rebase and not something the
> > users will type from the command line, won't it?
>
> There is not a huge lot of possible meanings of "seq" in the given
> context. Somehow I find it hard to believe someone will be confused by a
> backend command with a short name "seq" (seq-uence-something?)
It might be a bit confusing, since "seq" _produces_ sequences, and
"sequencer" is kind of an assembly line, getting commits in a sequence and
then applying the corresponding changes in order.
> It'll make the lines shorter, less need to wrap them.
By that reasoning, we should have git-a, git-b, ... but that would not
improve readability.
> BTW, what does "am" (git am) mean?
It means "applymbox", but that name was already taken. And "am" turned
out _not_ to replace "applymbox" right away as was expected, so it is a
bit of unfortunate history.
Ciao,
Dscho
^ permalink raw reply
* [PATCH] git-gui: Fix pre-commit hooks under MinGW/MSYS
From: Alexander N. Gavrilov @ 2008-07-05 11:38 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git, msysgit, Steffen Prohaska
From: Alexander Gavrilov <angavrilov@gmail.com>
Apply the work-around for checking the executable
permission of hook files not only on Cygwin, but on
Windows in general.
Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com>
---
This is a fix for msysgit issue #118.
(http://code.google.com/p/msysgit/issues/detail?id=118)
It may be possible to fix this in a better way, e.g. perhaps assume
that if the hook exists on Windows, then it's executable (msysgit
uses renaming to disable hooks), or at least check existance
before running the shell, but I don't know enough Tcl to do it.
-- Alexander
git-gui.sh | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-gui.sh b/git-gui.sh
index e6e8890..2d14bf2 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -473,10 +473,10 @@ proc githook_read {hook_name args} {
set pchook [gitdir hooks $hook_name]
lappend args 2>@1
- # On Cygwin [file executable] might lie so we need to ask
+ # On Windows [file executable] might lie so we need to ask
# the shell if the hook is executable. Yes that's annoying.
#
- if {[is_Cygwin]} {
+ if {[is_Windows]} {
upvar #0 _sh interp
if {![info exists interp]} {
set interp [_which sh]
--
1.5.3.3
^ permalink raw reply related
* [PATCH] Documentation: be precise about which date --pretty uses
From: Nikolaus Schulz @ 2008-07-05 0:00 UTC (permalink / raw)
To: git
This makes it explicit that the --pretty formats 'medium' and 'email' use the
author date (and ignore the committer date).
---
Documentation/pretty-formats.txt | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index ec37555..69e6d2f 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -30,7 +30,7 @@ This is designed to be as compact as possible.
commit <sha1>
Author: <author>
- Date: <date>
+ Date: <author date>
<title line>
@@ -50,9 +50,9 @@ This is designed to be as compact as possible.
commit <sha1>
Author: <author>
- AuthorDate: <date & time>
+ AuthorDate: <author date>
Commit: <committer>
- CommitDate: <date & time>
+ CommitDate: <committer date>
<title line>
@@ -62,7 +62,7 @@ This is designed to be as compact as possible.
From <sha1> <date>
From: <author>
- Date: <date & time>
+ Date: <author date>
Subject: [PATCH] <title line>
<full commit message>
--
1.5.6
^ permalink raw reply related
* About -X<option>
From: Johannes Schindelin @ 2008-07-05 12:57 UTC (permalink / raw)
To: gitster, git
Hi,
the -X<option> options are not really easy to convert to parseopt. This
is only one indication that the interface can be improved.
Another one: you are more likely wanting to pass backend-_specific_
options to git-merge.
So, how about using this syntax instead?
git pull -s recursive,theirs
i.e. strategy terms consist of the strategy name, optionally followed by a
comma separated list of backend options.
Hmm?
Ciao,
Dscho
^ permalink raw reply
* Re: About -X<option>
From: Miklos Vajna @ 2008-07-05 13:32 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: gitster, git
In-Reply-To: <alpine.DEB.1.00.0807051454060.3334@eeepc-johanness>
[-- Attachment #1: Type: text/plain, Size: 484 bytes --]
On Sat, Jul 05, 2008 at 02:57:12PM +0200, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> So, how about using this syntax instead?
>
> git pull -s recursive,theirs
>
> i.e. strategy terms consist of the strategy name, optionally followed by a
> comma separated list of backend options.
As a user I would think that it tells git-merge to first try 'recursive'
then 'theirs'.
Of course you can still say that users should read the documentation,
but... ;-)
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: About -X<option>
From: Pieter de Bie @ 2008-07-05 13:48 UTC (permalink / raw)
To: Miklos Vajna; +Cc: Johannes Schindelin, gitster, git
In-Reply-To: <20080705133245.GH4729@genesis.frugalware.org>
On 5 jul 2008, at 15:32, Miklos Vajna wrote:
> As a user I would think that it tells git-merge to first try
> 'recursive'
> then 'theirs'.
>
I agree with this. Perhaps there's an easy fix: how about a colon?
git pull -s recursive:theirs
might be more intuitive?
^ permalink raw reply
* [RFC/PATCH] Fix t7601-merge-pull-config.sh on AIX
From: Miklos Vajna @ 2008-07-05 14:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Johannes Schindelin, Olivier Marin, git, Mike Ralphson
The test failed on AIX (and likely other OS, such as apparently OSX)
where wc -l outputs whitespace.
Also, avoid unnecessary eval in conflict_count().
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Fri, Jul 04, 2008 at 06:49:03PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> In any case, this feels like an unnecessary use of eval. The call
> site
> you have look like this:
>
> conflict_count resolve_count
>
> but it is more natural if you are programming in shell to call it
> like:
>
> resolve_count=$(count_conflicts)
Changed.
> If you are going to do numerical comparison in later versions, you can
> just drop the dq around parameters of test:
>
> test $count_three = $count_two
Changed.
Mike, could you please confirm that this solves your problem as well?
t/t7601-merge-pull-config.sh | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 32585f8..95b4d71 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -70,10 +70,10 @@ test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octo
conflict_count()
{
- eval $1=`{
+ {
git diff-files --name-only
git ls-files --unmerged
- } | wc -l`
+ } | wc -l
}
# c4 - c5
@@ -115,15 +115,15 @@ test_expect_success 'merge picks up the best result' '
git config pull.twohead "recursive resolve" &&
git reset --hard c5 &&
git merge -s resolve c6
- conflict_count resolve_count &&
+ resolve_count=$(conflict_count) &&
git reset --hard c5 &&
git merge -s recursive c6
- conflict_count recursive_count &&
+ recursive_count=$(conflict_count) &&
git reset --hard c5 &&
git merge c6
- conflict_count auto_count &&
- test "$auto_count" = "$recursive_count" &&
- test "$auto_count" != "$resolve_count"
+ auto_count=$(conflict_count) &&
+ test $auto_count = $recursive_count &&
+ test $auto_count != $resolve_count
'
test_done
--
1.5.6.1.322.ge904b.dirty
^ permalink raw reply related
* [PATCH] Move 'stupid' merge strategy to contrib.
From: Miklos Vajna @ 2008-07-05 14:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, git, Johannes Schindelin
In-Reply-To: <20080704000701.GX4729@genesis.frugalware.org>
As pointed out by Linus, this strategy tries to take the best merge
base, but 'recursive' just does it better. If one needs something more
than 'resolve' then he/she should really use 'recursive' and not
'stupid'.
Given that it may still serve as a good example, don't remove it, just
move it to contrib/examples.
---
On Fri, Jul 04, 2008 at 02:07:01AM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:
> On Thu, Jul 03, 2008 at 04:54:31PM -0700, Junio C Hamano
> <gitster@pobox.com> wrote:
> > It is called stupid for a reason ;-).
> >
> > It has been sitting there as an example for a long time, and I do
> > not think anybody minds removing it.
>
> OK, then should I resend a patch that moves it to contrib/examples?
Here is one.
.gitignore | 1 -
Makefile | 3 +-
contrib/examples/git-merge-stupid.sh | 80 ++++++++++++++++++++++++++++++++++
git-merge-stupid.sh | 80 ----------------------------------
4 files changed, 81 insertions(+), 83 deletions(-)
create mode 100755 contrib/examples/git-merge-stupid.sh
delete mode 100755 git-merge-stupid.sh
diff --git a/.gitignore b/.gitignore
index 4ff2fec..8054d9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,7 +75,6 @@ git-merge-one-file
git-merge-ours
git-merge-recursive
git-merge-resolve
-git-merge-stupid
git-merge-subtree
git-mergetool
git-mktag
diff --git a/Makefile b/Makefile
index 78e08d3..bddd1a7 100644
--- a/Makefile
+++ b/Makefile
@@ -241,7 +241,6 @@ SCRIPT_SH += git-merge-octopus.sh
SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-merge.sh
-SCRIPT_SH += git-merge-stupid.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-parse-remote.sh
SCRIPT_SH += git-pull.sh
@@ -1429,7 +1428,7 @@ check-docs::
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
- git-merge-resolve | git-merge-stupid | git-merge-subtree | \
+ git-merge-resolve | git-merge-subtree | \
git-fsck-objects | git-init-db | \
git-?*--?* ) continue ;; \
esac ; \
diff --git a/contrib/examples/git-merge-stupid.sh b/contrib/examples/git-merge-stupid.sh
new file mode 100755
index 0000000..f612d47
--- /dev/null
+++ b/contrib/examples/git-merge-stupid.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+#
+# Resolve two trees, 'stupid merge'.
+
+# The first parameters up to -- are merge bases; the rest are heads.
+bases= head= remotes= sep_seen=
+for arg
+do
+ case ",$sep_seen,$head,$arg," in
+ *,--,)
+ sep_seen=yes
+ ;;
+ ,yes,,*)
+ head=$arg
+ ;;
+ ,yes,*)
+ remotes="$remotes$arg "
+ ;;
+ *)
+ bases="$bases$arg "
+ ;;
+ esac
+done
+
+# Give up if we are given two or more remotes -- not handling octopus.
+case "$remotes" in
+?*' '?*)
+ exit 2 ;;
+esac
+
+# Find an optimum merge base if there are more than one candidates.
+case "$bases" in
+?*' '?*)
+ echo "Trying to find the optimum merge base."
+ G=.tmp-index$$
+ best=
+ best_cnt=-1
+ for c in $bases
+ do
+ rm -f $G
+ GIT_INDEX_FILE=$G git read-tree -m $c $head $remotes \
+ 2>/dev/null || continue
+ # Count the paths that are unmerged.
+ cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l`
+ if test $best_cnt -le 0 -o $cnt -le $best_cnt
+ then
+ best=$c
+ best_cnt=$cnt
+ if test "$best_cnt" -eq 0
+ then
+ # Cannot do any better than all trivial merge.
+ break
+ fi
+ fi
+ done
+ rm -f $G
+ common="$best"
+ ;;
+*)
+ common="$bases"
+ ;;
+esac
+
+git update-index --refresh 2>/dev/null
+git read-tree -u -m $common $head $remotes || exit 2
+echo "Trying simple merge."
+if result_tree=$(git write-tree 2>/dev/null)
+then
+ exit 0
+else
+ echo "Simple merge failed, trying Automatic merge."
+ if git-merge-index -o git-merge-one-file -a
+ then
+ exit 0
+ else
+ exit 1
+ fi
+fi
diff --git a/git-merge-stupid.sh b/git-merge-stupid.sh
deleted file mode 100755
index f612d47..0000000
--- a/git-merge-stupid.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-#
-# Resolve two trees, 'stupid merge'.
-
-# The first parameters up to -- are merge bases; the rest are heads.
-bases= head= remotes= sep_seen=
-for arg
-do
- case ",$sep_seen,$head,$arg," in
- *,--,)
- sep_seen=yes
- ;;
- ,yes,,*)
- head=$arg
- ;;
- ,yes,*)
- remotes="$remotes$arg "
- ;;
- *)
- bases="$bases$arg "
- ;;
- esac
-done
-
-# Give up if we are given two or more remotes -- not handling octopus.
-case "$remotes" in
-?*' '?*)
- exit 2 ;;
-esac
-
-# Find an optimum merge base if there are more than one candidates.
-case "$bases" in
-?*' '?*)
- echo "Trying to find the optimum merge base."
- G=.tmp-index$$
- best=
- best_cnt=-1
- for c in $bases
- do
- rm -f $G
- GIT_INDEX_FILE=$G git read-tree -m $c $head $remotes \
- 2>/dev/null || continue
- # Count the paths that are unmerged.
- cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l`
- if test $best_cnt -le 0 -o $cnt -le $best_cnt
- then
- best=$c
- best_cnt=$cnt
- if test "$best_cnt" -eq 0
- then
- # Cannot do any better than all trivial merge.
- break
- fi
- fi
- done
- rm -f $G
- common="$best"
- ;;
-*)
- common="$bases"
- ;;
-esac
-
-git update-index --refresh 2>/dev/null
-git read-tree -u -m $common $head $remotes || exit 2
-echo "Trying simple merge."
-if result_tree=$(git write-tree 2>/dev/null)
-then
- exit 0
-else
- echo "Simple merge failed, trying Automatic merge."
- if git-merge-index -o git-merge-one-file -a
- then
- exit 0
- else
- exit 1
- fi
-fi
--
1.5.6.1.322.ge904b.dirty
^ permalink raw reply related
* [PATCH/v3] bundle.c: added --stdin option to git-bundle
From: Adam Brewster @ 2008-07-05 16:30 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Mark Levedahl, Junio C Hamano,
Jakub Narebski
Signed-off-by: Adam Brewster <asb@bu.edu>
---
It seems that the consensus is that the other half of my original
patch is no good. You have some pretty good ideas about how to
correctly address the problem I was trying to solve, and I look
forward to seeing them actually implemented.
For now, I offer separately the modification I made to bundle.c to
allow git-bundle to handle the --stdin option. There is no
accompanying change to the documentation because it already implies
that this option is available.
bundle.c | 22 ++++++++++++++++++++--
1 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/bundle.c b/bundle.c
index 0ba5df1..b44a4af 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,8 +227,26 @@ int create_bundle(struct bundle_header *header,
const char *path,
/* write references */
argc = setup_revisions(argc, argv, &revs, NULL);
- if (argc > 1)
- return error("unrecognized argument: %s'", argv[1]);
+
+ for (i = 1; i < argc; i++) {
+ if ( !strcmp(argv[i], "--stdin") ) {
+ char line[1000];
+ while (fgets(line, sizeof(line),
stdin) != NULL) {
+ int len = strlen(line);
+ if (len && line[len - 1] == '\n')
+ line[--len] = '\0';
+ if (!len)
+ break;
+ if (line[0] == '-')
+ die("options not supported in
--stdin mode");
+ if (handle_revision_arg(line, &revs, 0, 1))
+ die("bad revision '%s'", line);
+ }
+ continue;
+ }
+
+ return error("unrecognized argument: %s'", argv[i]);
+ }
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
--
1.5.5.1.211.g65ea3.dirty
^ permalink raw reply related
* Re: [PATCH/v3] bundle.c: added --stdin option to git-bundle
From: Jakub Narebski @ 2008-07-05 16:54 UTC (permalink / raw)
To: Adam Brewster; +Cc: git, Johannes Schindelin, Mark Levedahl, Junio C Hamano
In-Reply-To: <c376da900807050930i6d1da898s624be58adc6f1751@mail.gmail.com>
On Sat, 5 Jul 2008, Adam Brewster wrote:
> It seems that the consensus is that the other half of my original
> patch is no good. You have some pretty good ideas about how to
> correctly address the problem I was trying to solve, and I look
> forward to seeing them actually implemented.
It is not that the other half is "no good", it is rather that there
is no consensus how and in what way it should be implemented; be
it separate git-bases command, git-bundle--bases helper script, or
incorporated in git-bundle code; should it be written in Perl or
as shell script (only in case of git-bases or git-bundle--bases),
or should it be written in C.
Adding some documentation, with example usage (example "workflows")
would help adding git-bases to git core... perhaps at start send
it as script in contrib/ ?
> For now, I offer separately the modification I made to bundle.c to
> allow git-bundle to handle the --stdin option.
That's the way it is preferred here on git mailing list, to not bundle
non-controversial change with the one that needs (or seem to need) some
further discussion - do not hold features hostage ;-)
> There is no accompanying change to the documentation because it
> already implies that this option is available.
Adding an example of using `--stdin' to "[git-rev-list-args...]::"
in Documentation/git-bundle.txt would be good.
--
Jakub Narebski
Poland
^ permalink raw reply
* [PATCH v2 1/4] Add git-sequencer shell prototype
From: Stephan Beyer @ 2008-07-05 16:58 UTC (permalink / raw)
To: git
Cc: Christian Couder, Daniel Barkalow, Jakub Narebski,
Johannes Schindelin, Junio C Hamano, Stephan Beyer
In-Reply-To: <1214879914-17866-2-git-send-email-s-beyer@gmx.net>
git sequencer is planned as a backend for user scripts
that execute a sequence of git instructions and perhaps
need manual intervention, for example git-rebase or git-am.
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Hi,
this should now contain your suggestions (where I posted interdiffs)
and some further bugfixes of bugs I stumbled across.
(If you prefer an interdiff to v1, I can also send one...)
Regards,
Stephan
.gitignore | 1 +
Makefile | 1 +
command-list.txt | 1 +
git-sequencer.sh | 1921 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1924 insertions(+), 0 deletions(-)
create mode 100755 git-sequencer.sh
diff --git a/.gitignore b/.gitignore
index 4ff2fec..65a075b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,6 +111,7 @@ git-revert
git-rm
git-send-email
git-send-pack
+git-sequencer
git-sh-setup
git-shell
git-shortlog
diff --git a/Makefile b/Makefile
index 78e08d3..9db1a43 100644
--- a/Makefile
+++ b/Makefile
@@ -250,6 +250,7 @@ SCRIPT_SH += git-rebase--interactive.sh
SCRIPT_SH += git-rebase.sh
SCRIPT_SH += git-repack.sh
SCRIPT_SH += git-request-pull.sh
+SCRIPT_SH += git-sequencer.sh
SCRIPT_SH += git-sh-setup.sh
SCRIPT_SH += git-stash.sh
SCRIPT_SH += git-submodule.sh
diff --git a/command-list.txt b/command-list.txt
index 3583a33..44bb5b0 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -101,6 +101,7 @@ git-rev-parse ancillaryinterrogators
git-rm mainporcelain common
git-send-email foreignscminterface
git-send-pack synchingrepositories
+git-sequencer plumbingmanipulators
git-shell synchelpers
git-shortlog mainporcelain
git-show mainporcelain common
diff --git a/git-sequencer.sh b/git-sequencer.sh
new file mode 100755
index 0000000..e4e4aad
--- /dev/null
+++ b/git-sequencer.sh
@@ -0,0 +1,1921 @@
+#!/bin/sh
+# A git sequencer prototype.
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-sequencer [options] [--] [<file>]
+git-sequencer (--continue | --abort | --skip | --edit | --status)
+--
+ Options to start a sequencing process
+B,batch run in batch-mode
+onto= checkout the given commit or branch first
+q,quiet suppress output
+v,verbose be more verbose
+ Options to restart/change a sequencing process or show information
+continue continue paused sequencer process
+abort restore original branch and abort
+skip skip current patch and continue
+status show the status of the sequencing process
+edit invoke editor to let user edit the remaining insns
+ Options to be used by user scripts
+caller= provide information string: name|abort|cont|skip
+"
+
+. git-sh-setup
+require_work_tree
+
+git var GIT_COMMITTER_IDENT >/dev/null ||
+ die 'You need to set your committer info first'
+
+SEQ_DIR="$GIT_DIR/sequencer"
+TODO="$SEQ_DIR/todo"
+DONE="$SEQ_DIR/done"
+MSG="$SEQ_DIR/message"
+PATCH="$SEQ_DIR/patch"
+AUTHOR_SCRIPT="$SEQ_DIR/author-script"
+ORIG_AUTHOR_SCRIPT="$SEQ_DIR/author-script.orig"
+CALLER_SCRIPT="$SEQ_DIR/caller-script"
+WHY_FILE="$SEQ_DIR/why"
+MARK_PREFIX='refs/sequencer-marks'
+
+warn () {
+ echo "$*" >&2
+}
+
+cleanup () {
+ for ref in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+ do
+ git update-ref -d "$ref" "$ref" || return
+ done
+ rm -rf "$SEQ_DIR"
+}
+
+# Die if there has been a conflict
+die_to_continue () {
+ warn "$*"
+ test -z "$BATCHMODE" ||
+ die_abort 'Aborting, because of batch mode.'
+ test -z "$WHY" &&
+ echo 'conflict' >"$WHY_FILE" ||
+ echo "$WHY" >"$WHY_FILE"
+ exit 3
+}
+
+die_abort () {
+ restore
+ cleanup
+ die "$1"
+}
+
+quit () {
+ cleanup
+ test -z "$*" ||
+ echo "$*"
+ exit 0
+}
+
+perform () {
+ case "$VERBOSE" in
+ 0)
+ "$@" >/dev/null
+ ;;
+ 1)
+ output=$("$@" 2>&1 )
+ status=$?
+ test $status -ne 0 && printf '%s\n' "$output"
+ return $status
+ ;;
+ 2)
+ "$@"
+ ;;
+ esac
+}
+
+require_clean_work_tree () {
+ # test if working tree is dirty
+ git rev-parse --verify HEAD >/dev/null &&
+ git update-index --ignore-submodules --refresh &&
+ git diff-files --quiet --ignore-submodules &&
+ git diff-index --cached --quiet HEAD --ignore-submodules -- ||
+ die 'Working tree is dirty'
+}
+
+ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
+
+comment_for_reflog () {
+ if test -z "$ORIG_REFLOG_ACTION"
+ then
+ GIT_REFLOG_ACTION='sequencer'
+ test -z "$CALLER" ||
+ GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION ($CALLER)"
+ export GIT_REFLOG_ACTION
+ fi
+}
+
+# Get commit message from commit $1
+commit_message () {
+ git cat-file commit "$1" | sed -e '1,/^$/d'
+}
+
+LAST_COUNT=
+mark_action_done () {
+ sed -e 1q <"$TODO" >>"$DONE"
+ sed -e 1d <"$TODO" >"$TODO.new"
+ mv -f "$TODO.new" "$TODO"
+ if test "$VERBOSE" -gt 0
+ then
+ count=$(grep -c '^[^#]' <"$DONE")
+ total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")
+ if test "$LAST_COUNT" != "$count"
+ then
+ LAST_COUNT="$count"
+ test "$VERBOSE" -lt 1 ||
+ printf 'Sequencing (%d/%d)\r' "$count" "$total"
+ test "$VERBOSE" -lt 2 || echo
+ fi
+ fi
+}
+
+# Generate message, patch and author script files
+make_patch () {
+ parent_sha1=$(git rev-parse --verify "$1^" 2>/dev/null ||
+ echo '--root')
+ git diff-tree -p "$parent_sha1" "$1" >"$PATCH"
+ test -f "$MSG" ||
+ commit_message "$1" >"$MSG"
+ test -f "$AUTHOR_SCRIPT" ||
+ get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
+}
+
+# Generate a patch and die with "conflict" status code
+die_with_patch () {
+ make_patch "$1"
+ git rerere
+ die_to_continue "$2"
+}
+
+restore () {
+ git rerere clear
+
+ read HEADNAME <"$SEQ_DIR/head-name"
+ read HEAD <"$SEQ_DIR/head"
+ case $HEADNAME in
+ refs/*)
+ git symbolic-ref HEAD "$HEADNAME"
+ ;;
+ esac &&
+ perform git reset --hard "$HEAD"
+}
+
+has_action () {
+ grep '^[^#]' "$1" >/dev/null
+}
+
+# Check if text file $1 contains a commit message
+has_message () {
+ test -n "$(sed -n -e '/^Signed-off-by:/d;/^[^#]/p' <"$1")"
+}
+
+# Count parents of commit $1
+count_parents() {
+ git cat-file commit "$1" | sed -n -e '1,/^$/p' | grep -c '^parent'
+}
+
+# Evaluate the author script to get author information to
+# apply it with "with_author git foo" then.
+get_current_author () {
+ if test -f "$AUTHOR_SCRIPT"
+ then
+ . "$AUTHOR_SCRIPT"
+ else
+ . "$ORIG_AUTHOR_SCRIPT"
+ fi || die_abort 'Author script is damaged. This must not happen!'
+}
+
+# Run command with author information
+with_author () {
+ GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+ "$@"
+}
+
+# clean WHY_FILE and reset WHY
+clean_why () {
+ rm -f "$WHY_FILE"
+ WHY=
+}
+
+# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
+pick_one () {
+ what="$1"
+ shift
+
+ case "$what,$1" in
+ revert,*)
+ test "$1" != '--edit' &&
+ what='revert --no-edit'
+ ;;
+ cherry-pick,-*)
+ ;;
+ cherry-pick,*)
+ # fast forward
+ if test "$(git rev-parse --verify "$1^" 2>/dev/null)" = \
+ "$(git rev-parse --verify HEAD)"
+ then
+ perform git reset --hard "$1"
+ return
+ fi
+ ;;
+ esac
+ $use_perform git $what "$@"
+}
+
+nth_string () {
+ case "$1" in
+ *1[0-9]|*[04-9])
+ echo "$1th"
+ ;;
+ *1)
+ echo "$1st"
+ ;;
+ *2)
+ echo "$1nd"
+ ;;
+ *3)
+ echo "$1rd"
+ ;;
+ esac
+}
+
+make_squash_message () {
+ if test -f "$squash_msg"
+ then
+ count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+ <"$squash_msg" | sed -n -e '$p')+1))
+ echo "# This is a combination of $count commits."
+ sed -e '1d' -e '2,/^./{
+ /^$/d
+ }' <"$squash_msg"
+ else
+ count=2
+ echo '# This is a combination of 2 commits.'
+ echo '# The first commit message is:'
+ echo
+ commit_message HEAD
+ fi
+ echo
+ echo "# This is the $(nth_string "$count") commit message:"
+ echo
+ commit_message "$1"
+}
+
+make_squash_message_multiple () {
+ revlist=$(git rev-list --reverse "$sha1..HEAD")
+ count=$(echo "$revlist" | wc -l)
+ squash_i=0
+ echo "# This is a combination of $count commits."
+ for cur_sha1 in $revlist
+ do
+ squash_i=$(($squash_i+1))
+ if test -f "$squash_msg"
+ then
+ count=$(($count + $(sed -n -e \
+ 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+ <"$squash_msg" | sed -n -e '$p')+1))
+ sed -e '1d' -e '2,/^./{
+ /^$/d
+ }' <"$squash_msg"
+ fi
+ echo
+ echo "# This is the $(nth_string "$squash_i") commit message:"
+ echo
+ commit_message "$cur_sha1"
+ done
+}
+
+peek_next_command () {
+ sed -n -e '/^#/d' -e '1s/ .*$//p' <"$TODO"
+}
+
+# If $1 is a mark, make a ref from it; otherwise keep it.
+# Note on marks:
+# * :0 is allowed
+# * :01 is the same as :1
+mark_to_ref () {
+ arg="$1"
+ ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')
+ test -n "$ref" &&
+ arg="$MARK_PREFIX/$ref"
+ printf '%s\n' "$arg"
+}
+
+mark_to_commit () {
+ git rev-parse --verify "$(mark_to_ref "$1")"
+}
+
+
+cannot_fallback () {
+ echo "$1"
+ die_to_continue 'Cannot fall back to three-way merge. Please hand-edit.'
+}
+
+
+fallback_3way () {
+ O_OBJECT=$(cd "$GIT_OBJECT_DIRECTORY" && pwd)
+
+ rm -fr "$PATCH-merge-"*
+ mkdir "$PATCH-merge-tmp-dir"
+
+ # First see if the patch records the index info that we can use.
+ git apply --build-fake-ancestor "$PATCH-merge-tmp-index" "$PATCH" &&
+ GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+ git write-tree >"$PATCH-merge-base+" ||
+ cannot_fallback 'Repository lacks necessary blobs to fall back on 3-way merge.'
+
+ echo 'Using index info to reconstruct a base tree...'
+ if GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+ git apply --cached "$PATCH"
+ then
+ mv "$PATCH-merge-base+" "$PATCH-merge-base"
+ mv "$PATCH-merge-tmp-index" "$PATCH-merge-index"
+ else
+ cannot_fallback "Did you hand edit your patch?
+It does not apply to blobs recorded in its index."
+ fi
+
+ test -f "$PATCH-merge-index" &&
+ his_tree=$(GIT_INDEX_FILE="$PATCH-merge-index" git write-tree) &&
+ orig_tree=$(cat "$PATCH-merge-base") &&
+ rm -fr "$PATCH-merge-"* || exit 1
+
+ echo 'Falling back to patching base and 3-way merge...'
+
+ # This is not so wrong. Depending on which base we picked,
+ # orig_tree may be wildly different from ours, but his_tree
+ # has the same set of wildly different changes in parts the
+ # patch did not touch, so recursive ends up canceling them,
+ # saying that we reverted all those changes.
+
+ eval GITHEAD_$his_tree='"$firstline"'
+ export GITHEAD_$his_tree
+ git-merge-recursive "$orig_tree" -- HEAD "$his_tree" || {
+ git rerere
+ die 'Failed to merge in the changes.'
+ }
+}
+
+# Run hook "$@" (with arguments) if executable
+run_hook () {
+ test -z "$1" || return
+ hookname="$1"
+ hook="$GIT_DIR/hooks/$hookname"
+ shift
+ if test -x "$hook"
+ then
+ "$hook" "$@" ||
+ die_to_continue "Hook $hookname failed."
+ fi
+}
+
+# Add Signed-off-by: line if general option --signoff is given
+dashdash_signoff () {
+ add_signoff=
+ if test -n "$SIGNOFF"
+ then
+ last_signed_off_by=$(
+ sed -n -e '/^Signed-off-by: /p' <"$MSG" | sed -n -e '$p'
+ )
+ test "$last_signed_off_by" = "$SIGNOFF" ||
+ add_signoff=$(
+ test '' = "$last_signed_off_by" && echo
+ echo "$SIGNOFF"
+ )
+ fi
+ {
+ test -s "$MSG" && cat "$MSG"
+ test -n "$add_signoff" && echo "$add_signoff"
+ } >"$MSG.new"
+ mv "$MSG.new" "$MSG"
+}
+
+
+### --caller-related functions
+
+# Show string for caller invocation for --abort/--continue/--skip
+print_caller_info () {
+ case "$1" in
+ --abort)
+ echo "$CALLER_ABRT"
+ ;;
+ --continue)
+ echo "$CALLER_CONT"
+ ;;
+ --skip)
+ echo "$CALLER_SKIP"
+ ;;
+ *)
+ warn 'Internal error: Unknown print_caller argument!'
+ ;;
+ esac
+}
+
+# Print the program to invoke to (--)abort/continue/skip
+print_caller() {
+ caller_info=$(print_caller_info "$1")
+ if test -n "$CALLERCOMPARE" -a -n "$caller_info"
+ then
+ test -n "$CALLER" && printf "$CALLER "
+ echo "$caller_info"
+ else
+ echo "git sequencer $1"
+ fi
+}
+
+# Test if --caller was set correctly
+# $1 must be abort/continue/skip
+test_caller () {
+ caller_info=$(print_caller_info "--$1")
+ test -n "$CALLERCOMPARE" -a \
+ "$CALLERCOMPARE" != "$CALLERSTRING" -a \
+ -n "$caller_info" &&
+ die "You must use '$CALLER $caller_info' to $1!"
+}
+
+# Generate $CALLER_SCRIPT file from "git foo|--abort|--continue|--skip"
+# string $1
+generate_caller_script () {
+ echo "$1" | sed -e 's/^\(.*\)|\(.*\)|\(.*\)|\(.*\)$/\
+CALLERCOMPARE="\0"\
+CALLER="\1"\
+CALLER_ABRT="\2"\
+CALLER_CONT="\3"\
+CALLER_SKIP="\4"/' >"$CALLER_SCRIPT"
+}
+
+# Run caller script and set GIT_CHERRY_PICK_HELP
+assure_caller () {
+ test -r "$CALLER_SCRIPT" &&
+ . "$CALLER_SCRIPT"
+
+ GIT_CHERRY_PICK_HELP="
+After resolving the conflicts, mark the corrected paths with
+ git add <paths>
+or
+ git rm <paths>
+and run
+ $(print_caller --continue)
+Note, that your working tree must match the index."
+ export GIT_CHERRY_PICK_HELP
+}
+
+
+### Helpers for check_* functions:
+
+# Print a warning on todo checking
+todo_warn () {
+ printf 'Warning at line %d, %s: %s\n' "$line" "$command" "$*"
+}
+
+# Raise an error on todo checking
+todo_error () {
+ printf 'Error at line %d, %s: %s\n' "$line" "$command" "$*"
+ retval=1
+}
+
+# Test if $1 is a commit on todo checking
+commit_check () {
+ test "$(git cat-file -t "$1")" = commit ||
+ todo_error "'$1' is not a commit."
+}
+
+# A helper function to check if $1 is a an available mark during check_*
+arg_is_mark_check () {
+ for cur_mark in $available_marks
+ do
+ test "$cur_mark" -eq "${1#:}" && return
+ done
+ todo_error "Mark $1 is not yet defined."
+}
+
+# A helper function for check_* and mark usage:
+# check if "$1" is a commit or a defined mark
+arg_is_mark_or_commit_check () {
+ if expr "x$1" : 'x:[0-9][0-9]*$' >/dev/null
+ then
+ arg_is_mark_check "$1"
+ else
+ commit_check "$1"
+ fi
+}
+
+
+### Author script functions
+
+# Take "Name <e-mail>" in stdin and outputs author script
+make_author_script_from_string () {
+ sed -e "s/'/'"'\\'"''/g" \
+ -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME='\''\1'\''\
+GIT_AUTHOR_EMAIL='\''\2'\''\
+GIT_AUTHOR_DATE=/'
+}
+
+
+### General option functions (and options spec)
+
+OPTIONS_GENERAL=' General options:
+author= Override author
+C,reuse-commit= Reuse message and authorship data from commit
+F,file= Take commit message from given file
+m,message= Specify commit message
+M,reuse-message= Reuse message from commit
+signoff Add signoff
+e,edit Invoke editor to edit commit message'
+
+# Check if option is a general option
+check_general_option () {
+ general_shift=1
+ case "$1" in
+ --signoff)
+ return 0
+ ;;
+ --author)
+ general_shift=2
+ author_opt="t$author_opt"
+ expr "x$2" : 'x.* <.*>' >/dev/null ||
+ todo_error "Author \"$2\" not in the correct format \"Name <e-mail>\"."
+ ;;
+ -m)
+ general_shift=2
+ msg_opt="t$msg_opt"
+ ;;
+ -C)
+ general_shift=2
+ msg_opt="t$msg_opt"
+ author_opt="t$author_opt"
+ commit_check "$2"
+ ;;
+ -M)
+ general_shift=2
+ msg_opt="t$msg_opt"
+ commit_check "$2"
+ ;;
+ -F)
+ general_shift=2
+ msg_opt="t$msg_opt"
+ test -r "$2" ||
+ todo_error "Cannot read file '$2'."
+ ;;
+ -e)
+ test -z "$BATCHMODE" ||
+ todo_error '--batch and --edit options do not make sense together.'
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+# Set a general option variable or return 1
+handle_general_option () {
+ general_shift=1
+ case "$1" in
+ --signoff)
+ SIGNOFF=$(git-var GIT_COMMITTER_IDENT | sed -e '
+ s/>.*/>/
+ s/^/Signed-off-by: /')
+ ;;
+ --author)
+ general_shift=2
+ AUTHOR=t
+ echo "$2" |
+ make_author_script_from_string >"$AUTHOR_SCRIPT"
+ ;;
+ -m)
+ general_shift=2
+ MESSAGE="$2"
+ ;;
+ -C)
+ general_shift=2
+ AUTHOR=t
+ get_author_ident_from_commit "$2" >"$AUTHOR_SCRIPT"
+ MESSAGE=$(commit_message "$2")
+ ;;
+ -M)
+ general_shift=2
+ MESSAGE=$(commit_message "$2")
+ ;;
+ -F)
+ general_shift=2
+ MESSAGE=$(cat "$2")
+ ;;
+ -e)
+ EDIT=--edit
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+
+### Functions for checking and realizing TODO instructions
+# Note that options_*, check_* and insn_* function names are reserved.
+
+options_pause="\
+pause
+--
+"
+
+# Check the "pause" instruction
+check_pause () {
+ shift
+ test -z "$BATCHMODE" ||
+ todo_error '"pause" instruction and --batch do not make sense together.'
+ test $# -eq 0 ||
+ todo_error 'The pause instruction takes no arguments.'
+ return 0
+}
+
+# Realize the "pause" instruction
+insn_pause () {
+ mark_action_done
+ make_patch HEAD
+ if test "$VERBOSE" -gt 0
+ then
+ warn
+ warn 'You can now edit files and add them to the index.'
+ warn 'Once you are satisfied with your changes, run'
+ warn
+ warn " $(print_caller --continue)"
+ warn
+ warn 'If you only want to change the commit message, run'
+ warn 'git commit --amend before.'
+ fi
+ echo 'pause' >"$WHY_FILE"
+ exit 2
+}
+
+
+options_patch="\
+patch [options] <file>
+--
+3,3way Fall back to 3-way merge
+k Pass to git-mailinfo (keep subject)
+n Pass to git-mailinfo (no utf8)
+$OPTIONS_GENERAL
+ Options passed to git-apply:
+R,reverse Reverse changes
+context= Ensure context of ... lines
+p= Remove ... leading slashes
+unidiff-zero Bypass unidiff checks
+exclude= Do not apply changes to given files
+no-add Ignore additions of patch
+whitespace= Set whitespace error behavior
+inaccurate-eof Support inaccurate EOFs
+u no-op (backward compatibility)
+binary no-op (backward compatibility)
+"
+
+# Check the "file" instruction
+check_patch () {
+ while test $# -gt 1
+ do
+ case "$1" in
+ -3|-k|-n|-u|--binary|-R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+ :
+ ;;
+ -p|--whitespace|--exclude|--context)
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ check_general_option "$@" ||
+ todo_warn "Unknown option $1"
+ ;;
+ *)
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ break
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ if test -f "$1" -a -r "$1"
+ then
+ grep -e '^diff' "$1" >/dev/null ||
+ todo_error "File '$1' contains no patch."
+ else
+ todo_error "Cannot open file '$1'."
+ fi
+ return 0
+}
+
+# Realize the "patch" instruction
+insn_patch () {
+ comment_for_reflog patch
+
+ apply_opts=
+ mailinfo_opts=
+ threeway=
+
+ # temporary files
+ infofile="$SEQ_DIR/patch-info"
+ msgfile="$SEQ_DIR/patch-msg"
+
+ while test "$#" -gt 1
+ do
+ case "$1" in
+ -3)
+ threeway=t
+ ;;
+ -k|-n)
+ mailinfo_opts="$mailinfo_opts $1"
+ ;;
+ -u|--binary)
+ : Do nothing. It is there due to b/c only.
+ ;;
+ -R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+ apply_opts="$apply_opts $1"
+ ;;
+ -p)
+ shift
+ apply_opts="$apply_opts -p$1"
+ ;;
+ --whitespace|--exclude)
+ apply_opts="$apply_opts $1=$2"
+ shift
+ ;;
+ --context)
+ shift
+ apply_opts="$apply_opts -C$1"
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ handle_general_option "$@"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ filename="$1"
+
+ mark_action_done
+
+ git mailinfo $mailinfo_opts "$msgfile" "$PATCH" \
+ <"$filename" >"$infofile" ||
+ die_abort 'Could not read or parse mail'
+
+ # if author not set by option, read author information of patch
+ if test -z "$AUTHOR"
+ then
+ cp "$ORIG_AUTHOR_SCRIPT" "$AUTHOR_SCRIPT"
+ sed -e "s/'/'"'\\'"''/g" -n -e '
+ s/^Author: \(.*\)$/GIT_AUTHOR_NAME='\''\1'\''/p;
+ s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL='\''\1'\''/p;
+ s/^Date: \(.*\)$/GIT_AUTHOR_DATE='\''\1'\''/p
+ ' <"$infofile" >>"$AUTHOR_SCRIPT"
+ # If sed's result is empty, we keep the original
+ # author script by appending.
+ fi
+
+ # Ignore every mail that's not containing a patch
+ test -s "$PATCH" || {
+ warn 'Does not contain patch!'
+ return 0
+ }
+
+ edit_msg=
+ if grep -e '^Subject:' "$infofile" >/dev/null
+ then
+ # add subject to commit message
+ sed -n -e '/^Subject:/ s/Subject: //p' <"$infofile"
+ echo
+ echo
+ cat "$msgfile"
+ else
+ cat "$msgfile"
+ edit_msg=t
+ fi | git stripspace >"$MSG"
+ rm -f "$infofile" "$msgfile"
+
+ firstline=$(sed -e '1q' <"$MSG")
+
+ get_current_author
+
+ test -n "$MESSAGE" && printf '%s\n' "$MESSAGE" >"$MSG"
+ test -z "$firstline" && firstline=$(sed -e '1q' <"$MSG")
+
+ dashdash_signoff
+
+ with_author run_hook applypatch-msg "$MSG"
+ failed=
+ git apply $apply_opts --index "$PATCH" || failed=t
+
+ if test -n "$failed" -a -n "$threeway" && (with_author fallback_3way)
+ then
+ # Applying the patch to an earlier tree and merging the
+ # result may have produced the same tree as ours.
+ git diff-index --quiet --cached HEAD -- && {
+ echo 'No changes -- Patch already applied.'
+ return 0
+ # XXX: do we want that?
+ }
+ # clear apply_status -- we have successfully merged.
+ failed=
+ fi
+
+ if test -n "$failed"
+ then
+ die_to_continue 'Patch failed.'
+ # XXX: We actually needed a git-apply flag that creates
+ # conflict markers and sets the DIFF_STATUS_UNMERGED flag.
+ fi
+
+ with_author run_hook pre-applypatch
+
+ test -n "$EDIT" && edit_msg=t
+ if ! has_message "$MSG" || test -n "$edit_msg"
+ then
+ echo "
+# Please enter the commit message for the applied patch.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+ git_editor "$MSG" ||
+ die_with_patch 'Editor returned error.'
+ has_message "$MSG" ||
+ die_with_patch 'No commit message given.'
+ fi
+
+ tree=$(git write-tree) &&
+ parent=$(git rev-parse --verify HEAD) &&
+ commit=$(with_author git commit-tree "$tree" -p "$parent" <"$MSG") &&
+ git update-ref -m "$GIT_REFLOG_ACTION: $firstline" HEAD "$commit" "$parent" ||
+ die_to_continue 'Could not commit tree.'
+
+ test -x "$GIT_DIR/hooks/post-applypatch" &&
+ with_author "$GIT_DIR/hooks/post-applypatch"
+
+ return 0
+}
+
+
+options_pick="\
+pick [options] <commit>
+--
+R,reverse Revert introduced changes
+mainline= Specify parent number to use for merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "pick" instruction
+check_pick () {
+ mainline=
+ while test $# -gt 1
+ do
+ case "$1" in
+ -R)
+ ;;
+ --mainline)
+ shift
+ mainline="$1"
+ test "$mainline" -gt 0 || {
+ todo_error '--mainline needs an integer beginning from 1.'
+ mainline=
+ }
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ check_general_option "$@" ||
+ todo_warn "Unknown option $1"
+ ;;
+ *)
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ break
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ if test -n "$mainline"
+ then
+ parents=$(count_parents "$1")
+ test "$parents" -lt "$mainline" &&
+ todo_error "Commit has only $parents (less than $mainline) parents."
+ test "$parents" -eq 1 &&
+ todo_warn 'Commit is not a merge at all.'
+ fi
+
+ commit_check "$1"
+
+ return 0
+}
+
+# Realize the "pick" instruction
+insn_pick () {
+ op=cherry-pick
+ mainline=
+ while test $# -gt 1
+ do
+ case "$1" in
+ -R)
+ op=revert
+ ;;
+ --mainline)
+ mainline="$1 $2"
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ handle_general_option "$@"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ sha1=$(git rev-parse --verify "$1")
+
+ comment_for_reflog pick
+
+ mark_action_done
+
+ edit_msg="$EDIT"
+
+ # Don't edit on pick, but later, if author or message given.
+ test -n "$AUTHOR" -o -n "$MESSAGE" && edit_msg=
+
+ # Be kind to users and ignore --mainline=1 on non-merge commits
+ test -n "$mainline" -a 2 -gt $(count_parents "$sha1") && mainline=
+
+ use_perform=
+ test -n "$edit_msg" ||
+ use_perform=perform
+
+ pick_one "$op" $edit_msg $mainline $sha1 ||
+ die_with_patch $sha1 "Could not apply $sha1"
+
+ test -n "$EDIT" ||
+ use_perform=perform
+
+ get_current_author
+ signoff=
+ test -n "$SIGNOFF" && signoff=-s
+ if test -n "$AUTHOR" -a -n "$MESSAGE"
+ then
+ # this is just because we only want to do ONE amending commit
+ $use_perform git commit --amend $EDIT $signoff --no-verify \
+ --author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
+ --message="$MESSAGE"
+ elif test -n "$AUTHOR"
+ then
+ # correct author if AUTHOR is set
+ $use_perform git commit --amend $EDIT --no-verify -C HEAD \
+ --author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
+ elif test -n "$MESSAGE"
+ then
+ # correct commit message if MESSAGE is set
+ $use_perform git commit --amend $EDIT $signoff --no-verify \
+ -C HEAD --message="$MESSAGE"
+ elif test -n "$SIGNOFF"
+ then
+ # only add signoff
+ $use_perform git commit --amend $EDIT $signoff --no-verify \
+ -C HEAD
+ fi
+
+ return 0
+}
+
+options_edit="\
+edit <commit>
+--
+"
+
+# Check the "edit" instruction
+check_edit () {
+ shift
+ test -z "$BATCHMODE" ||
+ todo_error '"edit" instruction and --batch do not make sense together.'
+ test $# -eq 1 ||
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ return 0
+}
+
+# Realize the "edit" instruction
+insn_edit () {
+ shift
+ insn_pick "$1"
+
+ # work around mark_action_done in insn_pause
+ echo '# pausing' >"$TODO.new"
+ cat "$TODO" >>"$TODO.new"
+ mv -f "$TODO.new" "$TODO"
+ insn_pause
+}
+
+
+options_squash="\
+squash <commit>
+squash [options] --from <mark>
+--
+from Squash all commits from <mark>
+collect-signoffs Collect Signed-off-by: lines
+include-merges Do not fail on merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "squash" instruction
+check_squash () {
+ from=
+ collect=
+ merges=
+ while test $# -gt 1
+ do
+ case "$1" in
+ --from)
+ from=t
+ ;;
+ --collect-signoffs)
+ collect=t
+ todo_warn 'Not yet implemented.'
+ ;;
+ --include-merges)
+ merges=t
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ check_general_option "$@" ||
+ todo_error "Unknown option $1"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ # in --from mode?
+ if test -n "$from"
+ then
+ test -z "$merges" &&
+ cat "$DONE" "$TODO" |
+ sed -n -e '/^[ \t]*mark[ \t]*:\{0,1\}'"${1#:}"'\($\|[^0-9]\)/,'"$line"'p' |
+ grep '^[ \t]*merge' >/dev/null &&
+ todo_error "$1..HEAD contains a merge commit. You may try --include-merges."
+
+ arg_is_mark_check "$1"
+ else
+ test -n "$merges" &&
+ todo_error '--include-merges only makes sense with --from <mark>.'
+ test -n "$collect" &&
+ todo_error '--collect-signoffs only makes sense with --from <mark>.'
+
+ commit_check "$1"
+ fi
+
+ return 0
+}
+
+# Realize the "squash" instruction
+insn_squash () {
+ squash_msg="$MSG-squash"
+ from=
+ while test $# -gt 1
+ do
+ case "$1" in
+ --from)
+ from=t
+ ;;
+ --collect-signoffs)
+ warn '--collect-signoffs is not implemented.'
+ # XXX
+ ;;
+ --include-merges)
+ : # This has to be done during check_squash
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ handle_general_option "$@"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ if test -n "$from"
+ then
+ sha1=$(mark_to_commit ":${1#:}")
+ else
+ sha1=$(git rev-parse --verify "$1")
+ fi
+
+ comment_for_reflog squash
+
+ # Hm, somehow I don't think --skip on a conflicting squash
+ # may be useful, but if someone wants to do it, it should
+ # do the obvious: skip what squash would do.
+ echo "$(git rev-parse HEAD)" >"$SEQ_DIR/skiphead"
+
+ mark_action_done
+
+ if test -n "$MESSAGE"
+ then
+ printf '%s\n' "$MESSAGE" >"$MSG"
+ else
+ if test -n "$from"
+ then
+ make_squash_message_multiple "$sha1" >"$MSG"
+ else
+ make_squash_message "$sha1" >"$MSG"
+ fi
+ fi
+
+ case "$(peek_next_command)" in
+ squash)
+ edit_commit=
+ use_perform=perform
+ cp "$MSG" "$squash_msg"
+ ;;
+ *)
+ edit_commit=-e
+ use_perform=
+ rm -f "$squash_msg" || exit
+ ;;
+ esac
+
+ test -n "$MESSAGE" && edit_commit=
+ test -n "$EDIT" && edit_commit=-e
+
+ # is --author (or equivalent) set?
+ if test -n "$AUTHOR"
+ then
+ # evaluate author script
+ get_current_author
+ else
+ # if --author is not given, we get the authorship
+ # information from the commit before.
+ eval "$(get_author_ident_from_commit HEAD)"
+ # but we do not write an author script
+ fi
+
+ # --from or not
+ failed=
+ if test -n "$from"
+ then
+ perform git reset --soft "$sha1"
+ else
+ perform git reset --soft HEAD^
+
+ pick_one cherry-pick -n "$sha1" || failed=t
+ fi
+
+ dashdash_signoff
+
+ if test -z "$failed"
+ then
+ # This is like --amend, but with a different message
+ with_author $use_perform git commit --no-verify \
+ -F "$MSG" $edit_commit || failed=t
+ else
+ cp "$MSG" "$GIT_DIR/MERGE_MSG"
+ warn
+ warn "Could not apply $sha1..."
+ die_with_patch $sha1 ""
+ fi
+
+ return 0
+}
+
+
+options_mark="\
+mark <mark>
+--
+"
+
+# Check the "mark" instruction
+check_mark () {
+ shift
+ test $# -eq 1 ||
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ my_mark=$(expr "x${1#:}" : 'x0*\([0-9][0-9]*\)$')
+ test -n "$my_mark" ||
+ todo_error "Mark $1 not an integer."
+ expr "x$available_marks " : " $my_mark " >/dev/null &&
+ todo_error "Mark :$my_mark already defined. Choose another integer."
+ available_marks="$available_marks $my_mark"
+
+ return 0
+}
+
+# Realize the "mark" instruction
+insn_mark () {
+ shift
+ given="$1"
+
+ mark_action_done
+
+ mark=$(mark_to_ref ":${given#:}")
+ git update-ref "$mark" HEAD
+ return 0
+}
+
+
+options_merge="\
+merge [options] <commit-ish> ...
+--
+standard Generate default commit message
+s,strategy= Use merge strategy ...
+$OPTIONS_GENERAL
+"
+
+# Check the "merge" instruction
+check_merge () {
+ while test $# -gt 1
+ do
+ case "$1" in
+ --standard)
+ msg_opt="t$msg_opt"
+ ;;
+ -s)
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ check_general_option "$@" ||
+ todo_error "Unknown option $1"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ test $# -gt 0 ||
+ todo_error 'What are my parents? Need new parents!'
+
+ while test $# -gt 0
+ do
+ arg_is_mark_or_commit_check "$1"
+ shift
+ done
+ return 0
+}
+
+# Realize the "merge" instruction
+insn_merge () {
+ comment_for_reflog merge
+
+ standard=
+
+ while test $# -gt 1
+ do
+ case "$1" in
+ --standard)
+ standard=t
+ ;;
+ -s)
+ shift
+ strategy="-s $1"
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ handle_general_option "$@"
+ ;;
+ esac
+ shift $general_shift
+ done
+
+ new_parents=
+ for p in "$@"
+ do
+ new_parents="$new_parents $(mark_to_ref $p)"
+ done
+ new_parents="${new_parents# }"
+
+ get_current_author
+
+ if test -n "$standard"
+ then
+ for cur_parent in $new_parents
+ do
+ printf '%s\t\t%s' \
+ "$(git rev-parse "$cur_parent")" "$cur_parent"
+ done | git fmt-merge-msg >"$MSG"
+ fi
+
+ test -n "$MESSAGE" &&
+ printf '%s\n' "$MESSAGE" >"$MSG"
+
+ dashdash_signoff
+ if ! has_message "$MSG" || test -n "$EDIT"
+ then
+ echo "
+# Please enter the merge commit message.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+ git_editor "$MSG" ||
+ die_with_patch 'Editor returned error.'
+ has_message "$MSG" ||
+ die_with_patch 'No commit message given.'
+ fi
+
+ mark_action_done
+ if ! with_author perform git merge $strategy -m junk $new_parents
+ then
+ git rerere
+ cp "$MSG" "$GIT_DIR/MERGE_MSG"
+ die_to_continue 'Error merging'
+ fi
+ with_author perform git commit --amend -F "$MSG" --no-verify
+ return 0
+}
+
+
+options_reset="\
+reset <commit-ish>
+--
+"
+
+# Check the "reset" instruction
+check_reset () {
+ shift
+ test $# -eq 1 ||
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ arg_is_mark_or_commit_check "$1"
+
+ return 0
+}
+
+# Realize the "reset" instruction
+insn_reset () {
+ shift
+ comment_for_reflog reset
+
+ mark_action_done
+ perform git reset --hard "$(mark_to_commit "$1")"
+}
+
+
+options_ref="\
+ref <ref>
+--
+"
+
+# Check the "ref" instruction
+check_ref () {
+ shift
+ test $# -eq 1 ||
+ todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+ return 0
+}
+
+# Realize the "ref" instruction
+insn_ref () {
+ shift
+ comment_for_reflog ref
+
+ mark_action_done
+ perform git update-ref "$1" HEAD
+}
+
+
+### Instruction main loop
+
+# Run check_* or insn_* with massaged options
+# Usage: run_insn (check|do) <insn> <insn options>
+run_insn () {
+ c_or_i="$1"
+ insn="$2"
+ shift
+ shift
+ eval "option_spec=\"\$options_$insn\""
+ eval "$(printf "%s" "$option_spec" |
+ git rev-parse --parseopt -- "$@" ||
+ echo return)"
+ case "$c_or_i" in
+ check)
+ check_$insn "$@"
+ ;;
+ do)
+ insn_$insn "$@"
+ ;;
+ esac
+}
+
+# Execute the first line of the current TODO file
+execute_next () {
+ rm -f "$MSG"
+ echo 'HEAD' >"$SEQ_DIR/skiphead"
+ read command rol <"$TODO"
+ case "$command" in
+ '#'*|'')
+ mark_action_done
+ ;;
+ *)
+ test "$VERBOSE" -gt 1 &&
+ echo "Next line: $command $rol"
+
+ # reset general options
+ rm -f "$AUTHOR_SCRIPT"
+ general_shift=1
+ AUTHOR=
+ EDIT=
+ MESSAGE=
+ SIGNOFF=
+ # XXX: eval is evil!
+ eval "run_insn do $command $rol" ||
+ die_to_continue 'An unexpected error occured.'
+ ;;
+ esac
+}
+
+# Execute the rest of the TODO file and finish
+execute_rest () {
+ while has_action "$TODO"
+ do
+ execute_next
+ done
+
+ comment_for_reflog finish
+ if test -n "$ONTO"
+ then
+ git update-ref -m "$GIT_REFLOG_ACTION: $ONTO" "$ONTO" HEAD &&
+ git symbolic-ref HEAD "$ONTO"
+ fi &&
+ quit
+}
+
+
+# Main loop to check instructions
+todo_check () {
+ todo="$TODO"
+ test -n "$1" && todo="$1"
+
+ available_marks=' '
+ for cur_mark in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+ do
+ available_marks="$available_marks ${cur_mark##*/}"
+ done
+
+ retval=0
+ line=1
+ while read command rol
+ do
+ case "$command" in
+ '#'*|'')
+ ;;
+ *)
+ eval 'test -n "$options_'"$command"'"' || {
+ retval=1
+ todo_error "Unknown $command instruction"
+ continue
+ }
+
+ general_shift=1
+ msg_opt=
+ author_opt=
+ eval "run_insn check $command $rol" ||
+ todo_error "Unknown option used"
+ expr "$msg_opt" : 'ttt*' >/dev/null &&
+ todo_error 'You can only provide one commit message option.'
+ expr "$author_opt" : 'ttt*' >/dev/null &&
+ todo_error 'You can only provide one author option.'
+ ;;
+ esac
+ line=$(expr "$line" + 1)
+ done <"$todo"
+ return $retval
+}
+
+prepare_editable_todo () {
+ echo '# ALREADY DONE:'
+ sed -e 's/^/# /' <"$DONE"
+ echo '# '
+ echo "$markline"
+ cat "$TODO"
+}
+
+get_saved_options () {
+ read VERBOSE <"$SEQ_DIR/verbose"
+ read ONTO <"$SEQ_DIR/onto"
+ test -f "$WHY_FILE" &&
+ read WHY <"$WHY_FILE"
+ return 0
+}
+
+# Realize sequencer invocation
+do_startup () {
+ test -d "$SEQ_DIR" &&
+ die 'sequencer already started'
+
+ require_clean_work_tree
+
+ HEAD=$(git rev-parse --verify HEAD) ||
+ die 'No HEAD?'
+
+ mkdir "$SEQ_DIR" ||
+ die "Could not create temporary $SEQ_DIR"
+
+ # save options
+ echo "$VERBOSE" >"$SEQ_DIR/verbose"
+ test -n "$CALLERSTRING" &&
+ generate_caller_script "$CALLERSTRING"
+ # generate empty DONE and "onto" file
+ : >"$DONE"
+ : >"$SEQ_DIR/onto"
+
+ if test -n "$BATCHMODE"
+ then
+ GIT_CHERRY_PICK_HELP=' Aborting (batch mode)'
+ export GIT_CHERRY_PICK_HELP
+ else
+ assure_caller
+ fi
+
+ comment_for_reflog start
+
+ # save old head before checking out the given <branch>
+ git symbolic-ref HEAD >"$SEQ_DIR/head-name" 2>/dev/null ||
+ echo 'detached HEAD' >"$SEQ_DIR/head-name"
+ echo $HEAD >"$SEQ_DIR/head"
+ # do it here so that die_abort can work ;)
+
+ if test -n "$ONTO"
+ then
+ # if ONTO is a branch name, then keep it, otherwise
+ # we don't care anymore and erase ONTO.
+ if git-show-ref --quiet --verify -- "refs/heads/${ONTO##*/}"
+ then
+ ONTO="refs/heads/${ONTO##*/}"
+ echo "$ONTO" >"$SEQ_DIR/onto"
+ perform git checkout "$(git rev-parse "$ONTO")" ||
+ die_abort "Could not checkout branch $ONTO"
+ else
+ perform git checkout "$ONTO" ||
+ die_abort "Could not checkout commit $ONTO"
+ ONTO=
+ fi
+ fi
+
+ (git var GIT_AUTHOR_IDENT || git var COMMITTER_IDENT) |
+ make_author_script_from_string >"$ORIG_AUTHOR_SCRIPT"
+
+ # read from file or from stdin?
+ if test -z "$1"
+ then
+ : >"$TODO" ||
+ die_abort "Could not generate TODO file $TODO"
+ while read line
+ do
+ printf '%s\n' "$line" >>"$TODO" ||
+ die_abort "Could not append to TODO file $TODO"
+ done
+ else
+ cp "$1" "$TODO" ||
+ die_abort "Could not find TODO file $1."
+ fi
+
+ has_action "$TODO" || die_abort 'Nothing to do'
+ todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+ execute_rest
+ exit
+}
+
+# Realize --continue.
+do_continue () {
+ test -d "$SEQ_DIR" || die 'No sequencer running'
+ test_caller 'continue'
+
+ todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+ comment_for_reflog continue
+
+ # Sanity check
+ git rev-parse --verify HEAD >/dev/null ||
+ die_to_continue 'Cannot read HEAD'
+ git update-index --ignore-submodules --refresh &&
+ git diff-files --quiet --ignore-submodules ||
+ die_to_continue 'Working tree is dirty. (Use git add or git stash first?)'
+
+ get_saved_options
+
+ # do we have anything to commit? (staged changes)
+ if ! git diff-index --cached --quiet --ignore-submodules HEAD --
+ then
+ get_current_author
+
+ # After "pause", we should amend (merge-safe!).
+ # On conflict, we do not have a commit to amend, so we
+ # should just commit.
+ case "$WHY" in
+ pause)
+ with_author git commit --amend --no-verify -F "$MSG" -e ||
+ die_to_continue 'Could not commit staged changes.'
+ ;;
+ conflict)
+ with_author git commit --no-verify -F "$MSG" -e ||
+ die_to_continue 'Could not commit staged changes.'
+ echo '# resolved CONFLICTS of the last instruction' >>"$DONE"
+ ;;
+ *)
+ die_to_continue 'There are staged changes. Do not know what to do with them.'
+ esac
+ fi
+
+ require_clean_work_tree
+ clean_why
+ execute_rest
+ exit
+}
+
+# Realize --abort.
+do_abort () {
+ test -d "$SEQ_DIR" || die 'No sequencer running'
+ test_caller 'abort'
+
+ comment_for_reflog abort
+ restore
+ quit
+}
+
+# Realize --skip.
+do_skip () {
+ test -d "$SEQ_DIR" || die 'No sequencer running'
+ test_caller 'skip'
+
+ todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+ get_saved_options
+
+ comment_for_reflog skip
+ git rerere clear
+ clean_why
+
+ perform git reset --hard "$(cat "$SEQ_DIR/skiphead")" &&
+ echo '# SKIPPED the last instruction' >>"$DONE" &&
+ execute_rest
+ exit
+}
+
+# Realize --edit.
+do_edit () {
+ test -d "$SEQ_DIR" || die 'No sequencer running'
+
+ markline='### BEGIN EDITING BELOW THIS LINE ###'
+ prepare_editable_todo >"$TODO.new"
+
+ # XXX: does not make sense
+ # when input does not come from a terminal
+ git_editor "$TODO.new" ||
+ die 'Editor returned an error.'
+
+ if test -t 0 -a -t 1
+ then
+ echo
+ # interactive:
+ until has_action "$TODO.new" && todo_check "$TODO.new"
+ do
+ has_action "$TODO.new" || echo 'TODO file empty.'
+ printf 'What to do with the file? [c]orrect/[e]dit again/[r]ewind/[s]ave/[?] '
+ read reply
+ case "$reply" in
+ [cC]*)
+ git_editor "$TODO.new"
+ ;;
+ [eE]*)
+ prepare_editable_todo >"$TODO.new"
+ git_editor "$TODO.new"
+ ;;
+ [rRxXqQ]*)
+ rm -f "$TODO.new"
+ exit 0
+ ;;
+ [sS]*)
+ test "$WHY" != 'pause' ||
+ echo 'todo' >"$WHY_FILE"
+ break
+ ;;
+ [?hH]*)
+ cat <<EOF
+
+Help:
+s - save TODO file and exit
+c - respawn editor to correct TODO file
+e - drop changes and respawn editor on original TODO file
+r - drop changes and exit as if nothing happened
+? - print this help
+EOF
+ ;;
+ esac
+ echo
+ done
+ else
+ # defaults:
+ has_action "$TODO.new" || quit "Nothing to do"
+ todo_check || die 'TODO file contains errors. Aborting.'
+ fi
+ cp "$TODO" "$TODO.old"
+ if grep "^$markline" "$TODO.new" >/dev/null
+ then
+ sed -e "1,/^$markline/d" <"$TODO.new" >"$TODO"
+ else
+ cp "$TODO.new" "$TODO"
+ fi
+ rm -f "$TODO.new"
+ echo
+ echo 'TODO file contains:'
+ echo
+ cat "$TODO"
+ exit 0
+}
+
+# Realize --status.
+do_status () {
+ test -d "$SEQ_DIR" || die 'No sequencer running.'
+ get_saved_options
+
+ if has_action "$DONE"
+ then
+ echo 'Already done:'
+ sed -e 's/^/ /' <"$DONE"
+ echo
+ fi
+ case "$WHY" in
+ pause)
+ echo 'Intentionally paused.'
+ ;;
+ conflict)
+ echo 'Interrupted by conflict at'
+ sed -n -e 's/^/ /;$p' <"$DONE"
+ ;;
+ todo)
+ echo 'Interrupted because of errors in the TODO file.'
+ ;;
+ *)
+ echo 'Current state is broken.'
+ esac
+ test "$VERBOSE" -gt 1 && echo 'Running verbosely.'
+ test "$VERBOSE" -lt 1 && echo 'Running quietly.'
+ test -n "$ONTO" &&
+ echo "Sequencing on branch ${ONTO##*/}."
+ if has_action "$TODO"
+ then
+ echo
+
+ echo 'Still to do:'
+ sed -e 's/^/ /' <"$TODO"
+ fi
+ if test "$WHY" = todo
+ then
+ echo
+ echo 'But there are errors. To edit, run:'
+ echo ' git sequencer --edit'
+ else
+ echo
+ echo 'To abort & restore, invoke:'
+ echo " $(print_caller --abort)"
+ echo 'To continue, invoke:'
+ echo " $(print_caller --continue)"
+ test "$WHY" = 'pause' || {
+ echo 'To skip the current instruction, invoke:'
+ echo " $(print_caller --skip)"
+ }
+ fi
+ exit 0
+}
+
+is_standalone () {
+ test $# -eq 2 &&
+ test "$2" = '--' &&
+ test -z "$BATCHMODE" &&
+ test -z "$onto" &&
+ test "$VERBOSE" -eq 1
+}
+
+
+### Option handling and startup
+
+onto=
+BATCHMODE=
+CALLERSTRING=
+VERBOSE=1
+CALLER=
+CALLER_ABRT=
+CALLER_CONT=
+CALLER_SKIP=
+while test $# -gt 0
+do
+ case "$1" in
+ --continue)
+ is_standalone "$@" || usage
+ assure_caller
+ do_continue
+ ;;
+ --abort)
+ is_standalone "$@" || usage
+ assure_caller
+ do_abort
+ ;;
+ --skip)
+ is_standalone "$@" || usage
+ assure_caller
+ do_skip
+ ;;
+ --status)
+ is_standalone "$@" || usage
+ assure_caller
+ do_status
+ ;;
+ --edit)
+ is_standalone "$@" || usage
+ do_edit
+ ;;
+ --caller)
+ ###############################################################
+ # This feature is for user scripts only. (Hence undocumented.)
+ # User scripts should pass an argument like:
+ # --caller="git foo|abrt|go|next"
+ # on every git sequencer call. (It is only ignored on
+ # --edit and --status.)
+ # So git sequencer knows that
+ # "git foo abrt" will abort,
+ # "git foo go" will continue and
+ # "git foo next" will skip the sequencing process.
+ # This is useful if your user script does some extra
+ # preparations or cleanup before/after calling
+ # git sequencer --caller="..." --abort|--continue|--skip
+ #
+ # Running git-sequencer without the same --caller string
+ # fails then, until the sequencing process has finished or
+ # aborted.
+ #
+ # Btw, --caller="my_tiny_script.sh|-a||" will be
+ # interpreted, that users must invoke "my_tiny_script.sh -a"
+ # to abort, but can invoke "git sequencer --continue" and
+ # "git sequencer --skip" to continue or skip.
+ # And it is also possible to provide three different scripts
+ # by --caller="|script 1|tool 2|util 3".
+ #
+ # If your user script does not need any special
+ # abort/continue/skip behavior, then just do NOT pass
+ # the --caller option.
+ ###############################################################
+ shift
+ expr "x$1" : 'x.*|.*|.*|.*$' >/dev/null ||
+ die 'Wrong --caller format.'
+ CALLERSTRING="$1"
+ ;;
+ -B)
+ BATCHMODE=t
+ # XXX: we still have abort on editor invokations
+ ;;
+ --onto)
+ shift
+ ONTO="$1"
+ test $(git cat-file -t "$ONTO") = 'commit' ||
+ die "$ONTO is no commit or branch."
+ ;;
+ -q)
+ VERBOSE=0
+ # XXX: If there were no editors,
+ # we could just do exec >/dev/null
+ ;;
+ -v)
+ VERBOSE=2
+ # or increment it if we have several verbosity steps
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ die "$1 currently not implemented."
+ ;;
+ esac
+ shift
+done
+test $# -gt 1 && usage
+
+do_startup "$1"
--
1.5.6.361.g18ea7
^ permalink raw reply related
* [PATCH v2 2/4] Add git-sequencer prototype documentation
From: Stephan Beyer @ 2008-07-05 17:00 UTC (permalink / raw)
To: git
Cc: Christian Couder, Daniel Barkalow, Jakub Narebski,
Johannes Schindelin, Junio C Hamano, Stephan Beyer
In-Reply-To: <1214879914-17866-3-git-send-email-s-beyer@gmx.net>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
This contains some feedback of Jakub.
Documentation/git-sequencer.txt | 387 +++++++++++++++++++++++++++++++++++++++
1 files changed, 387 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-sequencer.txt
diff --git a/Documentation/git-sequencer.txt b/Documentation/git-sequencer.txt
new file mode 100644
index 0000000..1a9eda4
--- /dev/null
+++ b/Documentation/git-sequencer.txt
@@ -0,0 +1,387 @@
+git-sequencer(1)
+================
+
+NAME
+----
+git-sequencer - Execute a sequence of git instructions
+
+SYNOPSIS
+--------
+[verse]
+'git-sequencer' [--batch] [--onto=<base>] [--verbose|--quiet] [<file>]
+'git-sequencer' --continue | --skip | --abort | --edit | --status
+
+
+DESCRIPTION
+-----------
+Executes a sequence of git instructions to HEAD or `<base>`.
+The sequence is given by `<file>` or standard input.
+Also see 'TODO FILE FORMAT' below.
+
+Before doing anything, the TODO file is checked for correct syntax
+and sanity.
+
+In case of a conflict or request in the TODO file, git-sequencer will
+pause. On conflict you can use git-diff to locate the markers (`<<<<<<<`)
+and make edits to resolve the conflict.
+
+For each file you edit, you need to tell git the changes by doing
+
+ git add <file>
+
+After resolving the conflict manually and updating the index with the
+desired resolution, you can continue the sequencing process with
+
+ git sequencer --continue
+
+Alternatively, you can undo the git-sequencer progress with
+
+ git sequencer --abort
+
+or skip the current instruction with
+
+ git sequencer --skip
+
+or correct the TODO file with
+
+ git sequencer --edit
+
+During pauses or when finished with the sequencing task, the current
+HEAD will always be the result of the last processed instruction.
+
+
+OPTIONS
+-------
+<file>::
+ Filename of the TODO file. If omitted, standard input is used.
+ See 'TODO FILE FORMAT' below.
+
+-B::
+--batch::
+ Run in batch mode. If unexpected user intervention is needed
+ (e.g. a conflict or the need to run an editor), git-sequencer fails.
++
+Note that the sanity check fails, if you use this option
+and an instruction like `edit` or `pause` is in the TODO file.
+
+--onto=<base>::
+ Checkout given commit or branch before sequencing.
+ If you provide a branch, sequencer will make the provided
+ changes on the branch, i.e. the branch will be changed.
+
+--continue::
+ Restart the sequencing process after having resolved a merge conflict.
+
+--abort::
+ Restore the original branch and abort the sequence operation.
+
+--skip::
+ Restart the sequencing process by skipping the current instruction.
+
+--status::
+ Show the current status of git-sequencer and what
+ operations can be done to change that status.
+
+--edit::
+ Invoke editor to edit the unprocessed part of the TODO file.
++
+The file is syntax- and sanity-checked afterwards, so that you can
+safely run `git sequencer --skip` or `--continue` after editing.
+If you nonetheless noticed that you made a mistake, you can
+overwrite `.git/sequencer/todo` with `.git/sequencer/todo.old` and
+rerun `git sequencer --edit`.
++
+If the check fails you are prompted if you want to correct your
+changes, edit again, cancel editing or really want to save.
+
+-q::
+--quiet::
+ Suppress output.
+ (Not yet implemented.)
+
+-v::
+--verbose::
+ Be more verbose.
+
+
+NOTES
+-----
+
+When sequencing, it is possible, that you are changing the history of
+a branch in a way that can cause problems for anyone who already has
+a copy of the branch in their repository and tries to pull updates from
+you. You should understand the implications of using git-sequencer on
+a repository that you share.
+
+git-sequencer will usually be called by another git porcelain, like
+linkgit:git-am[1] or linkgit:git-rebase[1].
+
+
+TODO FILE FORMAT
+----------------
+
+The TODO file contains basically one instruction per line.
+
+Blank lines will be ignored.
+All characters after a `#` character will be ignored until the end of a line.
+
+The following instructions can be used:
+
+
+edit <commit>::
+ Picks a commit and pauses the sequencer process to let you
+ make changes.
++
+This is a short form for `pick <commit> and `pause` on separate lines.
+
+
+mark <mark>::
+ Set a symbolic mark for the last commit.
+ `<mark>` is an unsigned integer starting at 1 and
+ prefixed with a colon, e.g. `:1`.
++
+The marks can help if you want to refer to commits that you
+created during the sequencer process, e.g. if you want to
+merge such a commit.
++
+The set marks are removed after the sequencer has completed.
+
+
+merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
+ Merge commits into HEAD.
++
+You can refer to a commit by a mark.
++
+If you do not provide a commit message (using `-F`, `-m`, `-C`, `-M`,
+or `--standard`), an editor will be invoked.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+ --standard;;
+ Generates a commit message like 'Merge ... into HEAD'.
+ See also linkgit:git-fmt-merge-msg[1].
+
+ -s <strategy>;;
+ --strategy=<strategy>;;
+ Use the given merge strategy.
+ See also linkgit:git-merge[1].
+
+
+pick [options] <commit>::
+ Pick (see linkgit:git-cherry-pick[1]) a commit.
+ Sequencer will pause on conflicts.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+ -R;;
+ --reverse;;
+ Revert the changes introduced by pick <commit>.
+
+ --mainline=<n>;;
+ Allows you to pick merge commits by specifying the
+ parent number (beginning from 1) to let sequencer
+ replay the changes relative to the specified parent.
+ +
+This option does not work together with `-R`.
+
+
+patch [options] <file>::
+ If file `<file>` is a pure (diff) patch, then apply the patch.
+ If no `--message` option is given, an editor will
+ be invoked to enter a commit message.
++
+If `<file>` is a linkgit:git-format-patch[1]-formatted patch,
+then the patch will be commited.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+ -3;;
+ --3way;;
+ When the patch does not apply cleanly, fall back on
+ 3-way merge, if the patch records the identity of blobs
+ it is supposed to apply to, and we have those blobs
+ available locally.
+
+ -k;;
+ Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+ -n;;
+ Pass `-n` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+ --exclude=<path-pattern>;;
+ Do not apply changes to files matching the given path pattern.
+ This can be useful when importing patchsets, where you want to
+ exclude certain files or directories.
+
+ -R;;
+ --reverse;;
+ Apply the patch in reverse.
+
+ --no-add;;
+ When applying a patch, ignore additions made by the
+ patch. This can be used to extract the common part between
+ two files by first running `diff` on them and applying
+ the result with this option, which would apply the
+ deletion part but not addition part.
+
+ --whitespace=<action>;;
+ Specify behavior on whitespace errors.
+ See linkgit:git-apply[1] for a detailed description.
+
+ --context=<n>;;
+ Ensure at least <n> lines of surrounding context match before
+ and after each change. When fewer lines of surrounding
+ context exist they all must match. By default no context is
+ ever ignored.
+
+ --inaccurate-eof;;
+ Under certain circumstances, some versions of diff do not
+ correctly detect a missing new-line at the end of the file.
+ As a result, patches created by such diff programs do not
+ record incomplete lines correctly.
+ This option adds support for applying such patches by
+ working around this bug.
+
+ -p<n>;;
+ Remove <n> leading slashes from traditional diff paths.
+ The default is 1.
+
+ --unidiff-zero;;
+ By default, linkgit:git-apply[1] expects that the patch being
+ applied is a unified diff with at least one line of context.
+ This provides good safety measures, but breaks down when
+ applying a diff generated with --unified=0. To bypass these
+ checks use '--unidiff-zero'.
+
+
+pause::
+ Pauses the sequencer process to let you manually make changes.
+ For example, you can re-edit the done commit, fix bugs or typos,
+ or you can make further commits on top of HEAD before continuing.
++
+After you have finished your changes and added them to the index,
+invoke `git-sequencer --continue`.
+If you only want to edit the last commit message with an editor,
+run `git commit --amend` (see linkgit:git-commit[1]) before saying
+`--continue`.
+
+
+ref <ref>::
+ Set ref `<ref>` to the current HEAD, see also
+ linkgit:git-update-ref[1].
+
+
+reset <commit-ish>::
+ Go back (see linkgit:git-reset[1] `--hard`) to commit `<commit-ish>`.
+ `<commit-ish>` can also be given by a mark, if prefixed with a colon.
+
+
+squash [options] <commit>::
+ Add the changes introduced by `<commit>` to the last commit.
++
+See 'GENERAL OPTIONS' for values of `options`.
+
+squash [options] --from <mark>::
+ Squash all commits from the given mark into one commit.
+ There must not be any `merge` instructions between the
+ `mark` instruction and this `squash --from` instruction.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+ --collect-signoffs;;
+ Collect the Signed-off-by: lines of each commit and
+ add them to the squashed commit message.
+ (Not yet implemented.)
+
+ --include-merges;;
+ Sanity check does not fail if you have merges
+ between HEAD and <mark>.
+
+
+GENERAL OPTIONS
+---------------
+
+Besides some special options, the instructions
+`patch`, `merge`, `pick`, `squash` take the following general options:
+
+--author=<author>::
+ Override the author name and e-mail address used in the commit.
+ Use `A U Thor <author@example.com>` format.
+
+-C <commit-ish>::
+--reuse-commit=<commit-ish>::
+ Reuse message and authorship data from specified commit.
+
+-M <commit-ish>
+--reuse-message=<commit-ish>::
+ Reuse message from specified commit.
+ Note, that only the commit message is reused
+ and not the authorship information.
+
+-F <file>::
+--file=<file>::
+ Take the commit message from the given file.
+
+-m <msg>::
+--message=<msg>::
+ Use the given `<msg>` as the commit message.
+
+--signoff::
+ Add `Signed-off-by:` line to the commit message (if not yet there),
+ using the committer identity of yourself.
+
+-e::
+--edit::
+ Regardless what commit message options are given,
+ invoke the editor to allow editing of the commit message.
+
+
+RETURN VALUES
+-------------
+
+git-sequencer returns:
+
+* `0`, if git-sequencer successfully completed all the instructions
+ in the TODO file or successfully aborted after
+ `git sequencer --abort`,
+* `2`, on user-requested pausing, e.g.
+ when using the `edit` instruction.
+* `3`, on pauses that are not requested, e.g.
+ when there are conflicts to resolve
+ or errors in the TODO file.
+* any other value on error, e.g.
+ running git-sequencer on a bare repository.
+
+
+EXAMPLES
+--------
+
+TODO [Here the usage of all commands should become clear.]
+
+
+SEE ALSO
+--------
+
+linkgit:git-add[1],
+linkgit:git-am[1],
+linkgit:git-cherry-pick[1],
+linkgit:git-commit[1],
+linkgit:git-fmt-merge-msg[1],
+linkgit:git-format-patch[1],
+linkgit:git-rebase[1],
+linkgit:git-reset[1],
+linkgit:git-update-ref[1]
+
+
+Authors
+-------
+Written by Stephan Beyer <s-beyer@gmx.net>.
+
+
+Documentation
+-------------
+Documentation by Stephan Beyer and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
--
1.5.6.361.g18ea7
^ permalink raw reply related
* Re: [PATCH] git daemon: avoid calling syslog() from a signal handler
From: Junio C Hamano @ 2008-07-05 17:26 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Brian Foster, git
In-Reply-To: <alpine.DEB.1.00.0807051201320.3334@eeepc-johanness>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> So you will check every 1ms to see if there are new dead children, but why
>> is this necessary?
>
> This comes from me not reading the man page for poll() properly. Of
> course, I want to check every second: syslog timestamps the messages with
> a resolution of 1 second, AFAIR, or at least some of them do.
Hmm.
The question was not about the millisecond typo, but about why time-out at
all.
We would need to somehow break out of poll() after handling the SIGCHLD
signal and I guess timing the syscall out would be the most obvious way,
but somehow it felt awkward.
Another way would be to set up a pipe to ourself that is included in the
poll() and write a byte to the pipe from the signal handler.
^ permalink raw reply
* Re: [RFC/PATCH 3/4] Add git-sequencer test suite (t3350)
From: Stephan Beyer @ 2008-07-05 17:31 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Schindelin
In-Reply-To: <1214879914-17866-4-git-send-email-s-beyer@gmx.net>
Hi,
I've just noticed that those tag names:
+ git tag A &&
+ git tag B &&
+ git tag C &&
+ git tag D &&
+ git tag E &&
+ git tag F &&
+ git tag I &&
+ref refs/tags/CE
are perhaps a bad idea, because they could also be an abbreviated SHA1.
As it seems, git does not think this is ambiguous, since the exact tag
name exists.
So it could make sense to add a non-[0-9A-Fa-f] prefix, but it does not
seem to be necessary. Hmmm, so this mail is just a mental note :)
Regards,
Stephan
--
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F
^ permalink raw reply
* Re: About -X<option>
From: Junio C Hamano @ 2008-07-05 17:40 UTC (permalink / raw)
To: Pieter de Bie; +Cc: Miklos Vajna, Johannes Schindelin, gitster, git
In-Reply-To: <AB745D70-D23A-4742-A5B3-DC1B6CAD9C30@ai.rug.nl>
Pieter de Bie <frimmirf@gmail.com> writes:
> On 5 jul 2008, at 15:32, Miklos Vajna wrote:
>
>> As a user I would think that it tells git-merge to first try
>> 'recursive'
>> then 'theirs'.
>>
>
> I agree with this. Perhaps there's an easy fix: how about a colon?
>
> git pull -s recursive:theirs
>
> might be more intuitive?
How would you do the equivalent of
git pull -s recursive -Xsubtree="My Playpen/" -Xours
with the syntax?
Note that the current scripted "pull" and "merge" has a limitation that it
does not allow $IFS in -X<option> but that is not a designed-in limitation
but purely comes from a lazy implementation .
Also I do not see why -X<option> is not easy with parseopt() as Dscho
claimed in the original message in this thread...
^ permalink raw reply
* Re: [PATCH/v3] bundle.c: added --stdin option to git-bundle
From: Junio C Hamano @ 2008-07-05 18:15 UTC (permalink / raw)
To: Adam Brewster
Cc: git, Johannes Schindelin, Mark Levedahl, Junio C Hamano,
Jakub Narebski
In-Reply-To: <c376da900807050930i6d1da898s624be58adc6f1751@mail.gmail.com>
"Adam Brewster" <adambrewster@gmail.com> writes:
> Subject: Re: [PATCH/v3] bundle.c: added --stdin option to git-bundle
When the change is not about implementation detail (in which case you do
want to name the source file and perhaps even a function name), but about
a new feature that is visible to the end-users of a command, we'd want the
message talk in terms of what the new feature does, not how the new
feature is invoked nor where it is implemented. In other words, something
like these are preferred:
git-bundle: add --stdin
Teach git-bundle to read tips and basis from standard input
and don't say "You did" in past tense --- say things in imperative mood
instead, as if you are commanding the person who applies the patch to make
it happen. Older log entries in our history (e.g. "git log -n 20 v0.99")
may give you a better feel.
And give a few lines of obvious justfication in the body of the commit log
message, e.g.
This patch allows the caller to feed the revision parameters to
git-bundle from its standard input. This way, a script do not
have to worry about limitation of the length of command line.
to explain why this is good. In order to explain that you may have to
talk about other things (like what it does and how it does it), but keep
in mind that the primary thing you should talk about is _why_.
> ... because it already implies that this option is available.
If that is the case, please mention in the commit log message something
like "Even though the documentation said "bundle --stdin" is accepted it
didn't. This patch teaches the option to the command".
But I do not think there is no such implication. "bundle create" may take
list of positive and negative refs as arguments or --branches, but it does
not take (and it shouldn't -- I do not think it should take --bisect
option, for example) artibrary options that rev-list command accepts.
> bundle.c | 22 ++++++++++++++++++++--
> 1 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/bundle.c b/bundle.c
> index 0ba5df1..b44a4af 100644
> --- a/bundle.c
> +++ b/bundle.c
> @@ -227,8 +227,26 @@ int create_bundle(struct bundle_header *header,
> const char *path,
Wrapped.
> /* write references */
> argc = setup_revisions(argc, argv, &revs, NULL);
> - if (argc > 1)
> - return error("unrecognized argument: %s'", argv[1]);
> +
> + for (i = 1; i < argc; i++) {
> + if ( !strcmp(argv[i], "--stdin") ) {
Style.
> + char line[1000];
> + while (fgets(line, sizeof(line),
> stdin) != NULL) {
Too deep indentation. Wrapped.
> + int len = strlen(line);
> + if (len && line[len - 1] == '\n')
> + line[--len] = '\0';
> + if (!len)
> + break;
> + if (line[0] == '-')
> + die("options not supported in
> --stdin mode");
> + if (handle_revision_arg(line, &revs, 0, 1))
> + die("bad revision '%s'", line);
> + }
> + continue;
> + }
> +
> + return error("unrecognized argument: %s'", argv[i]);
> + }
Having said that, I think copying and pasting read_revisions_from_stdin()
in bundle.c is a wrong approach to take. Probably the function can easily
be split out of builtin-rev-list.c and moved to revision.c or somewhere
(which will be the first patch), and then a separate patch can add a few
lines to call it from bundle.c.
^ permalink raw reply
* Re: [RFC/PATCH 3/4] Add git-sequencer test suite (t3350)
From: Junio C Hamano @ 2008-07-05 18:16 UTC (permalink / raw)
To: Stephan Beyer; +Cc: git, Johannes Schindelin
In-Reply-To: <20080705173130.GA8099@leksak.fem-net>
Stephan Beyer <s-beyer@gmx.net> writes:
> Hi,
>
> I've just noticed that those tag names:
> + git tag A &&
> + git tag B &&
> + git tag C &&
> + git tag D &&
> + git tag E &&
> + git tag F &&
> + git tag I &&
> +ref refs/tags/CE
> are perhaps a bad idea, because they could also be an abbreviated SHA1.
I do not think we take a single letter abbrev so it should be fine.
^ permalink raw reply
* Re: Git, merging, and News/Relnotes files
From: Linus Torvalds @ 2008-07-05 18:38 UTC (permalink / raw)
To: Edward Z. Yang; +Cc: Git Mailing List
In-Reply-To: <g4n7j6$359$1@ger.gmane.org>
On Sat, 5 Jul 2008, Edward Z. Yang wrote:
>
> Something that I've noticed recently, as we've started migrating away
> from the ghetto SVN development model to the Git branchy model, is that
> this NEWS file ends up being the source of a lot of conflicts. Granted,
> they're easy conflicts to resolve, but still, they make a pull a little
> more complicated than it should be.
I don't think anybody really _uses_ the functionality, but git does have
the capability to specify special merge drivers based on filenames.
So you can
(a) create a merge strategy that automaticaly does what you want. There's
a built-in driver called "union" that may or may not work for your
use case.
See "Defining a custom merge driver" in "man gitattributes" for more
details about this.
(b) Say which files you want to merge with this driver, by having
something like
NEWS merge=news-merge
in your .gitattributes file (or in ".git/info/attributes", if you
want to keep this all private to your own setup rather than in a
committed file that gets distributed to everybody else too).
and now your NEWS file will be merged using your special "news-merge"
custom merge function.
Of course, the custom merge driver is only done for non-trivial merges.
Git will do all the trivial fast-forward merges on its own, and only call
the custom merge driver for things that have actual possible data
conflicts (ie changes in both branches).
NOTE! Keeping an ordered list (like a ChangeLog or a NEWS file) is
fundamentally not an easy thing to do in a distributed environment. The
"union" merge strategy may well work for you (and if it does, this is all
going to be very easy), but it's also entirely possible that you will find
that the ordering in a distributed environment is so unspecified, you'll
prefer to do the merges by hand _anyway_ in the end.
So the first thing you should do is probably to just *try* adding that
NEWS merge=union
line to your .gitattributes file, and see if it works for you. My personal
guess is that you'll realize that you really prefer doing the trivial
merges manually after all, but hey, maybe not. And as mentioned, you *can*
create your very own merge strategy that knows about the particular rules
of the files in question, but that gets more complex.
For example, the default 'union' merge will literally _duplicate_
identical that were added in both branches. So if you cherry-pick a commit
so that it exists both in the branch you are merging _and_ the branch you
are merging into, then any additions to the NEWS file will basically show
up twice, and yet auto-merge "cleanly".
Which is very understandable, but is almost certainly not what you want.
Linus
^ permalink raw reply
* Re: Git, merging, and News/Relnotes files
From: Edward Z. Yang @ 2008-07-05 19:07 UTC (permalink / raw)
To: git; +Cc: torvalds, pdebie
In-Reply-To: <alpine.LFD.1.10.0807051119170.2815@woody.linux-foundation.org>
Linus Torvalds wrote:
> So the first thing you should do is probably to just *try* adding that
>
> NEWS merge=union
>
> line to your .gitattributes file, and see if it works for you.
Sounds like a good first-step. It's very unlikely that we're going to
bother writing our own merge strategy for the NEWS file, so if union
ends up being more trouble than its worth, we'll probably end sticking
with manual merges.
Pieter also suggested (for some reason, I don't see the post on this
list) the git-merge-changelog driver from Gnu Savannah. Unfortunately,
the log format is a little different from ours (entries are sorted into
BC-incompatible, features, bugfixes and internal changes), so the driver
may not work (it's still worth a try, I imagine).
I'm slightly surprised no one suggested that I can the file, given that
both Git and the Linux kernel don't have one.
> For example, the default 'union' merge will literally _duplicate_
> identical that were added in both branches. So if you cherry-pick a commit
> so that it exists both in the branch you are merging _and_ the branch you
> are merging into, then any additions to the NEWS file will basically show
> up twice, and yet auto-merge "cleanly".
I suppose that's why we have git reset --hard HEAD~. :-) I will
certainly keep this gotcha in mind.
^ permalink raw reply
* Re: Git, merging, and News/Relnotes files
From: Linus Torvalds @ 2008-07-05 20:03 UTC (permalink / raw)
To: Edward Z. Yang; +Cc: pdebie, Git Mailing List
In-Reply-To: <486FC65C.70602@thewritingpot.com>
On Sat, 5 Jul 2008, Edward Z. Yang wrote:
>
> I'm slightly surprised no one suggested that I can the file, given that
> both Git and the Linux kernel don't have one.
Well, I personally think ChangeLog files are a total waste of time. You're
much better off autogenerating those from the real logs, I think.
[ Although in all honesty, I also think we could improve on our reporting
tools, and have ways to perhaps highlight big or important changes some
way ]
But a NEWS file that actually talks about new features is a different
thing. It can make lots of sense to maintain something like that, so I
wouldn't suggest canning it if it works for you. I'm not convinced it
would work for the kernel, but I suspect it can work really well for other
projects.
> > For example, the default 'union' merge will literally _duplicate_
> > identical that were added in both branches. So if you cherry-pick a commit
> > so that it exists both in the branch you are merging _and_ the branch you
> > are merging into, then any additions to the NEWS file will basically show
> > up twice, and yet auto-merge "cleanly".
>
> I suppose that's why we have git reset --hard HEAD~. :-) I will
> certainly keep this gotcha in mind.
Well, the real problem with a clean automatic merge is not that it can't
be undone (or better yet - fixed: just edit the NEWS file and then do a
"git commit --amend NEWS" to fix up the atomatic merge), but the fact that
most of the time you'll simply never even notice.
IOW, when something merges cleanly (and the 'union' merge will basically
always do so), the most common case is probably that people won't even
_look_ at the end result - especially if it works fine most of the time.
That's why a trivial conflict can often be better than a silently clean
merge: at least it forces people to spend a small amount of brainpower to
look at the obvious fix.
But hey, give it a try. Maybe you'll like the union merge, together with
occasional manual fixups. Or maybe you'll decide that a specialized merge
strategy isn't that painful after all (or can find somebody who already
went through the pain and wrote one you can use).
Linus
^ permalink raw reply
* [PATCH] git-bundle: add --stdin
From: Adam Brewster @ 2008-07-05 20:40 UTC (permalink / raw)
To: git
Cc: gitster, mdl123, Johannes.Schindelin, jnareb, adambrewster,
Adam Brewster
In-Reply-To: <1215290434-27694-2-git-send-email-adambrewster@gmail.com>
Teach git-bundle to read revision arguments from stdin like git-rev-list.
This patch allows the caller to feed the revision parameters to git-bundle
from its standard input. This way, a script do not have to worry about
limitation of the length of command line.
Documentation/git-bundle.txt says that git-bundle takes arguments acceptable
to git-rev-list. Obviously some arguments that git-rev-list handles don't
make sense for git-bundle (e.g. --bisect) but --stdin is pretty reasonable.
Signed-off-by: Adam Brewster <asb@bu.edu>
---
bundle.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)
mode change 100644 => 100755 bundle.c
diff --git a/bundle.c b/bundle.c
old mode 100644
new mode 100755
index 0ba5df1..00b2aab
--- a/bundle.c
+++ b/bundle.c
@@ -178,6 +178,7 @@ int create_bundle(struct bundle_header *header, const char *path,
int i, ref_count = 0;
char buffer[1024];
struct rev_info revs;
+ int read_from_stdin = 0;
struct child_process rls;
FILE *rls_fout;
@@ -227,8 +228,16 @@ int create_bundle(struct bundle_header *header, const char *path,
/* write references */
argc = setup_revisions(argc, argv, &revs, NULL);
- if (argc > 1)
- return error("unrecognized argument: %s'", argv[1]);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--stdin")) {
+ if (read_from_stdin++)
+ die("--stdin given twice?");
+ read_revisions_from_stdin(&revs);
+ continue;
+ }
+ return error("unrecognized argument: %s'", argv[i]);
+ }
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
--
1.5.5.1.211.g65ea3.dirty
^ permalink raw reply related
* [PATCH v4 0/3]
From: Adam Brewster @ 2008-07-05 20:40 UTC (permalink / raw)
To: git; +Cc: gitster, mdl123, Johannes.Schindelin, jnareb, adambrewster
In-Reply-To: <7vod5crydx.fsf@gitster.siamese.dyndns.org>
Sorry for the idiotic wrapping problems in my last email.
Previously, I was trying to keep from changing any of the important stuff,
like git-rev-list, but I should know better than cut-and-pasting code.
As requested, I've broken the change into a multiple of patches. First moving
read_revisions_from_stdin to revision.c, next modifying git-bundle to handle
--stdin, and finally a patch adding my old git-basis to contrib.
I think I've corrected all of the style issues you pointed out, and I've also
tried to craft more informative commit messages.
^ permalink raw reply
* [PATCH] Move read_revisions_from_stdin from builtin-rev-list.c to revision.c
From: Adam Brewster @ 2008-07-05 20:40 UTC (permalink / raw)
To: git
Cc: gitster, mdl123, Johannes.Schindelin, jnareb, adambrewster,
Adam Brewster
In-Reply-To: <1215290434-27694-1-git-send-email-adambrewster@gmail.com>
Some other commands might like to support the --stdin option like
git-rev-list. Since they don't want to depend on builtin-rev-list, the
function has to be somewhere else.
Signed-off-by: Adam Brewster <asb@bu.edu>
---
builtin-rev-list.c | 17 -----------------
revision.c | 17 +++++++++++++++++
2 files changed, 17 insertions(+), 17 deletions(-)
mode change 100644 => 100755 builtin-rev-list.c
mode change 100644 => 100755 revision.c
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
old mode 100644
new mode 100755
index 11a7eae..b4a2c44
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -575,23 +575,6 @@ static struct commit_list *find_bisection(struct commit_list *list,
return best;
}
-static void read_revisions_from_stdin(struct rev_info *revs)
-{
- char line[1000];
-
- while (fgets(line, sizeof(line), stdin) != NULL) {
- int len = strlen(line);
- if (len && line[len - 1] == '\n')
- line[--len] = 0;
- if (!len)
- break;
- if (line[0] == '-')
- die("options not supported in --stdin mode");
- if (handle_revision_arg(line, revs, 0, 1))
- die("bad revision '%s'", line);
- }
-}
-
int cmd_rev_list(int argc, const char **argv, const char *prefix)
{
struct commit_list *list;
diff --git a/revision.c b/revision.c
old mode 100644
new mode 100755
index 5a1a948..0191160
--- a/revision.c
+++ b/revision.c
@@ -911,6 +911,23 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
return 0;
}
+void read_revisions_from_stdin(struct rev_info *revs)
+{
+ char line[1000];
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+ int len = strlen(line);
+ if (len && line[len - 1] == '\n')
+ line[--len] = '\0';
+ if (!len)
+ break;
+ if (line[0] == '-')
+ die("options not supported in --stdin mode");
+ if (handle_revision_arg(line, revs, 0, 1))
+ die("bad revision '%s'", line);
+ }
+}
+
static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
{
if (!revs->grep_filter) {
--
1.5.5.1.211.g65ea3.dirty
^ 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