* Re: Can I remove stg sync --undo ?
From: Karl Hasselström @ 2008-07-08 4:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <b0943d9e0807071347n17e2e09ai761b849d2d03bc9c@mail.gmail.com>
On 2008-07-07 21:47:13 +0100, Catalin Marinas wrote:
> 2008/7/5 Karl Hasselström <kha@treskal.com>:
>
> > On 2008-07-04 23:05:11 +0100, Catalin Marinas wrote:
> >
> > > There are two operations that can conflict for sync - pushing a
> > > patch and the actual sync'ing, i.e. a three-way merge with the
> > > patch to be synchronised with (kind of fold).
> >
> > My guess is that conflicts of the first type would work out of the
> > box (i.e. they'd get an extra log entry) while conflicts of the
> > second type would not.
>
> I don't really care as long as I can get back to the patch state
> before running the sync command if anything goes wrong. So, one undo
> level would be enough.
>
> > We need a sync undo test.
>
> Yes but not sure what how undo would behave in this situation yet.
OK, then I'll write a simple test that makes sure undo and sync
interact the way I imagine they should. ;-)
> > With just "stg undo" (or reset or redo), you get the usual
> > new-infrastructure check about dirty index and working tree (the
> > whole index must be clean, and the parts of the worktree we need
> > to touch must be clean). Which prevents you from undoing a
> > conflicting push, for example.
> >
> > With the --hard flag, any uncleanliness in index or worktree
> > simply gets zapped (just like with git reset --hard). I'm not 100%
> > happy with this -- what I'd really like is to zap only the files
> > that we need to touch. But I haven't figured out a good way to do
> > that.
>
> OK, not much to comment here, it's your implementation :-).
But you too get to live with the design decisions ...
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* [PATCH] Documentation: mention ORIG_HEAD in am, merge, and rebase
From: Brian Gernhardt @ 2008-07-08 4:12 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano
In-Reply-To: <35BBB0D4-B3E1-4097-AF11-E0F6223125EA@silverinsanity.com>
Merge has always set ORIG_HEAD but never mentioned it, while we
recently added it to am and rebase. These facts should be reflected
in the documentation.
git-reset also sets ORIG_HEAD, but that fact is already mentioned in
the very first example so no changes were needed there.
Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
---
Documentation/git-am.txt | 6 ++++++
Documentation/git-merge.txt | 4 +++-
Documentation/git-rebase.txt | 3 ++-
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 3863eeb..88ca5f1 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -145,6 +145,12 @@ directory exists, so if you decide to start over from scratch,
run `rm -f -r .dotest` before running the command with mailbox
names.
+Before any patches are applied, ORIG_HEAD is set to the tip of the
+current branch. This is useful if you have problems with multiple
+commits, like running 'git am' on the wrong branch or an error in the
+commits that is more easily fixed by changing the mailbox (e.g.
+errors in the "From:" lines).
+
SEE ALSO
--------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 62f99b5..019e4ca 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -81,7 +81,9 @@ Otherwise, merge will refuse to do any harm to your repository
(that is, it may fetch the objects from remote, and it may even
update the local branch used to keep track of the remote branch
with `git pull remote rbranch:lbranch`, but your working tree,
-`.git/HEAD` pointer and index file are left intact).
+`.git/HEAD` pointer and index file are left intact). In addition,
+merge always sets `.git/ORIG_HEAD` to the original state of HEAD so
+a problematic merge can be removed by using `git reset ORIG_HEAD`.
You may have local modifications in the working tree files. In
other words, 'git-diff' is allowed to report changes.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f3459c7..37382c4 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -26,7 +26,8 @@ of commits that would be shown by `git log <upstream>..HEAD`.
The current branch is reset to <upstream>, or <newbase> if the
--onto option was supplied. This has the exact same effect as
-`git reset --hard <upstream>` (or <newbase>).
+`git reset --hard <upstream>` (or <newbase>). This includes setting
+ORIG_HEAD to the pre-rebase tip of the branch.
The commits that were previously saved into the temporary area are
then reapplied to the current branch, one by one, in order. Note that
--
1.5.6.2.393.g45096
^ permalink raw reply related
* Re: [PATCH 1/3] git-imap-send: Allow the program to be run from subdirectories of a git tree.
From: Jeff King @ 2008-07-08 4:10 UTC (permalink / raw)
To: Rob Shearman; +Cc: git
In-Reply-To: <1096648c0807070105l49a6dc38ra1710b0aadb220d9@mail.gmail.com>
On Mon, Jul 07, 2008 at 09:05:22AM +0100, Rob Shearman wrote:
> Call setup_git_directory_gently to allow git-imap-send to be used from
> subdirectories of a git tree.
> [...]
> + setup_git_directory_gently( NULL );
I don't think this is right; now we _must_ be in a git tree to run the
command, which was not the case previously. You need to pass a non-NULL
parameter to setup_git_directory_gently, which is where it will store
the "are we in a git dir" return value (even if you don't want to look
at it, if the parameter is NULL, it will die("not a git repository")).
-Peff
^ permalink raw reply
* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Lea Wiemann @ 2008-07-08 4:08 UTC (permalink / raw)
To: Jakub Narebski
Cc: git, Sam Vilain, Joshua Roys, Sverre Rabbelier, Sverre Rabbelier,
David Symonds, John Hawley, Marek Zawirski, Shawn O. Pearce,
Miklos Vajna, Johannes Schindelin, Stephan Beyer,
Christian Couder, Daniel Barkalow
In-Reply-To: <200807080227.43515.jnareb@gmail.com>
Jakub Narebski wrote:
> 3. Gitweb caching
I'll post a complete status update in the next few days. And three
large patches (including the mechanize test). ;-)
> Lea has chosen caching data and memcached as primary caching engine,
> and is working on object layer on top of Git.pm, namely Git::Repo and
> friends, which will be used by gitweb. If I understand correctly
> caching is to be done, or at least helped by this layer.
That's correct, except that I'm not using Git.pm anywhere; Git::Repo is
independent of Git.pm. More about that later...
^ permalink raw reply
* Re: error: unlink(.git/refs/remotes/origin/testbranch) failed: was remote does not support deleting refs
From: Jeff King @ 2008-07-08 4:08 UTC (permalink / raw)
To: Dmitry Potapov; +Cc: Junio C Hamano, Martin, git
In-Reply-To: <37fcd2780807061134l341ac676ueb674a976ce15e6f@mail.gmail.com>
On Sun, Jul 06, 2008 at 10:34:31PM +0400, Dmitry Potapov wrote:
> On Sun, Jul 06, 2008 at 02:20:18AM +0200, Martin wrote:
> > But I get another error:
> > $ git push origin :testbranch
> > To ssh://myserver.com/my/path/to/repository
> > - [deleted] testbranch
> > error: unlink(.git/refs/remotes/origin/testbranch) failed: No such file_
> > or directory
> > error: Failed to delete
> >_
> > Any idea?
>
> It is harmless. It is just that "git push origin :refs/heads/testbranch"
> cannot remove your local reference to that branch because you already have
> removed it by running "git branch -d -r origin/testbranch"
It is harmless, but it still feels a little wrong to scare the user with
that message, especially since "Failed to delete" is ambiguous; it looks
like the main operation, deleting the remote ref, failed. But it didn't;
the operation that failed was something not even explicitly asked for.
How about this cleanup:
-- >8 --
make deleting a missing ref more quiet
If git attempts to delete a ref, but the unlink of the ref
file fails, we print a message to stderr. This is usually a
good thing, but if the error is ENOENT, then it indicates
that the ref has _already_ been deleted. And since that's
our goal, it doesn't make sense to complain to the user.
This harmonizes the error reporting behavior for the
unpacked and packed cases; the packed case already printed
nothing on ENOENT, but the unpacked printed unconditionally.
Additionally, send-pack would, when deleting the tracking
ref corresponding to a remote delete, print "Failed to
delete" on any failure. This can be a misleading
message, since we actually _did_ delete at the remote side,
but we failed to delete locally. Rather than make the
message more precise, let's just eliminate it entirely; the
delete_ref routine already takes care of printing out a much
more specific message about what went wrong.
Signed-off-by: Jeff King <peff@peff.net>
---
builtin-send-pack.c | 3 +--
refs.c | 2 +-
t/t5404-tracking-branches.sh | 7 +++++++
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index d76260c..a708d0a 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -226,8 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
if (args.verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (ref->deletion) {
- if (delete_ref(rs.dst, NULL))
- error("Failed to delete");
+ delete_ref(rs.dst, NULL);
} else
update_ref("update by push", rs.dst,
ref->new_sha1, NULL, 0, 0);
diff --git a/refs.c b/refs.c
index 6c6e9e5..39a3b23 100644
--- a/refs.c
+++ b/refs.c
@@ -925,7 +925,7 @@ int delete_ref(const char *refname, const unsigned char *sha1)
i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
err = unlink(lock->lk->filename);
- if (err) {
+ if (err && errno != ENOENT) {
ret = 1;
error("unlink(%s) failed: %s",
lock->lk->filename, strerror(errno));
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
index 1493a92..64fe261 100755
--- a/t/t5404-tracking-branches.sh
+++ b/t/t5404-tracking-branches.sh
@@ -10,6 +10,7 @@ test_expect_success 'setup' '
git commit -m 1 &&
git branch b1 &&
git branch b2 &&
+ git branch b3 &&
git clone . aa &&
git checkout b1 &&
echo b1 >>file &&
@@ -50,4 +51,10 @@ test_expect_success 'deleted branches have their tracking branches removed' '
test "$(git rev-parse origin/b1)" = "origin/b1"
'
+test_expect_success 'already deleted tracking branches ignored' '
+ git branch -d -r origin/b3 &&
+ git push origin :b3 >output 2>&1 &&
+ ! grep error output
+'
+
test_done
--
1.5.6.2.381.ga86b
^ permalink raw reply related
* [StGit PATCH 2/2] Test for "stg edit"
From: Karl Hasselström @ 2008-07-08 4:03 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20080708035750.23134.75833.stgit@yoghurt>
We weren't testing this comaratively complicated command at all. (And
not surprisingly, some corner cases that should have worked didn't.)
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
t/t3300-edit.sh | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 205 insertions(+), 0 deletions(-)
create mode 100755 t/t3300-edit.sh
diff --git a/t/t3300-edit.sh b/t/t3300-edit.sh
new file mode 100755
index 0000000..8304d9f
--- /dev/null
+++ b/t/t3300-edit.sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+test_description='Test "stg edit"'
+
+. ./test-lib.sh
+
+test_expect_success 'Setup' '
+ printf "000\n111\n222\n" >> foo &&
+ git add foo &&
+ git commit -m "Initial commit" &&
+ sed -i "s/000/000xx/" foo &&
+ git commit -a -m "First change" &&
+ sed -i "s/111/111yy/" foo &&
+ git commit -a -m "Second change" &&
+ sed -i "s/222/222zz/" foo &&
+ git commit -a -m "Third change" &&
+ stg init &&
+ stg uncommit -n 3 p &&
+ stg pop
+'
+
+# Commit parse functions.
+msg () { git cat-file -p $1 | sed '1,/^$/d' | tr '\n' / | sed 's,/*$,,' ; }
+auth () { git log -n 1 --pretty=format:"%an, %ae" $1 ; }
+date () { git log -n 1 --pretty=format:%ai $1 ; }
+
+test_expect_success 'Edit message of top patch' '
+ test "$(msg HEAD)" = "Second change" &&
+ stg edit p2 -m "Second change 2" &&
+ test "$(msg HEAD)" = "Second change 2"
+'
+
+test_expect_success 'Edit message of non-top patch' '
+ test "$(msg HEAD^)" = "First change" &&
+ stg edit p1 -m "First change 2" &&
+ test "$(msg HEAD^)" = "First change 2"
+'
+
+test_expect_success 'Edit message of unapplied patch' '
+ test "$(msg $(stg id p3))" = "Third change" &&
+ stg edit p3 -m "Third change 2" &&
+ test "$(msg $(stg id p3))" = "Third change 2"
+'
+
+test_expect_success 'Set patch message with --file <file>' '
+ test "$(msg HEAD)" = "Second change 2" &&
+ echo "Pride or Prejudice" > commitmsg &&
+ stg edit p2 -f commitmsg &&
+ test "$(msg HEAD)" = "Pride or Prejudice"
+'
+
+test_expect_success 'Set patch message with --file -' '
+ echo "Pride and Prejudice" | stg edit p2 -f - &&
+ test "$(msg HEAD)" = "Pride and Prejudice"
+'
+
+( printf 'From: A U Thor <author@example.com>\nDate: <omitted>'
+ printf '\n\nPride and Prejudice' ) > expected-tmpl
+omit_date () { sed "s/^Date:.*$/Date: <omitted>/" ; }
+
+test_expect_success 'Save template to file' '
+ stg edit --save-template saved-tmpl p2 &&
+ omit_date < saved-tmpl > saved-tmpl-d &&
+ test_cmp expected-tmpl saved-tmpl-d
+'
+
+test_expect_success 'Save template to stdout' '
+ stg edit --save-template - p2 > saved-tmpl2 &&
+ omit_date < saved-tmpl2 > saved-tmpl2-d &&
+ test_cmp expected-tmpl saved-tmpl2-d
+'
+
+# Test the various ways of invoking the interactive editor. The
+# preference order should be
+#
+# 1. GIT_EDITOR
+# 2. stgit.editor (legacy)
+# 3. core.editor
+# 4. VISUAL
+# 5. EDITOR
+# 6. vi
+
+mkeditor ()
+{
+ cat > "$1" <<EOF
+#!/bin/sh
+printf "\n$1\n" >> "\$1"
+EOF
+ chmod a+x "$1"
+}
+
+mkeditor vi
+test_expect_failure 'Edit commit message interactively (vi)' '
+ m=$(msg HEAD) &&
+ PATH=.:$PATH stg edit p2 &&
+ test "$(msg HEAD)" = "$m/vi"
+'
+
+mkeditor e1
+test_expect_success 'Edit commit message interactively (EDITOR)' '
+ m=$(msg HEAD) &&
+ EDITOR=./e1 PATH=.:$PATH stg edit p2 &&
+ echo $m && echo $(msg HEAD) &&
+ test "$(msg HEAD)" = "$m/e1"
+'
+
+mkeditor e2
+test_expect_failure 'Edit commit message interactively (VISUAL)' '
+ m=$(msg HEAD) &&
+ VISUAL=./e2 EDITOR=./e1 PATH=.:$PATH stg edit p2 &&
+ test "$(msg HEAD)" = "$m/e2"
+'
+
+mkeditor e3
+test_expect_failure 'Edit commit message interactively (core.editor)' '
+ m=$(msg HEAD) &&
+ git config core.editor e3 &&
+ VISUAL=./e2 EDITOR=./e1 PATH=.:$PATH stg edit p2 &&
+ test "$(msg HEAD)" = "$m/e3"
+'
+
+mkeditor e4
+test_expect_success 'Edit commit message interactively (stgit.editor)' '
+ m=$(msg HEAD) &&
+ git config stgit.editor e4 &&
+ VISUAL=./e2 EDITOR=./e1 PATH=.:$PATH stg edit p2 &&
+ test "$(msg HEAD)" = "$m/e4"
+'
+
+mkeditor e5
+test_expect_failure 'Edit commit message interactively (GIT_EDITOR)' '
+ m=$(msg HEAD) &&
+ GIT_EDITOR=./e5 VISUAL=./e2 EDITOR=./e1 PATH=.:$PATH stg edit p2 &&
+ test "$(msg HEAD)" = "$m/e5"
+'
+
+rm -f vi e1 e2 e3 e4 e5
+git config --unset core.editor
+git config --unset stgit.editor
+
+mkeditor twoliner
+test_expect_failure 'Both noninterative and interactive editing' '
+ EDITOR=./twoliner stg edit -e -m "oneliner" p2 &&
+ test "$(msg HEAD)" = "oneliner/twoliner"
+'
+rm -f twoliner
+
+cat > diffedit <<EOF
+#!/bin/sh
+sed -i 's/111yy/111YY/' "\$1"
+EOF
+chmod a+x diffedit
+test_expect_success 'Edit patch diff' '
+ EDITOR=./diffedit stg edit -d p2 &&
+ test "$(grep 111 foo)" = "111YY"
+'
+rm -f diffedit
+
+test_expect_success 'Sign a patch' '
+ m=$(msg HEAD) &&
+ stg edit --sign p2 &&
+ test "$(msg HEAD)" = "$m//Signed-off-by: C O Mitter <committer@example.com>"
+'
+
+test_expect_success 'Acknowledge a patch' '
+ m=$(msg HEAD^) &&
+ stg edit --ack p1 &&
+ test "$(msg HEAD^)" = "$m//Acked-by: C O Mitter <committer@example.com>"
+'
+
+test_expect_success 'Set author' '
+ stg edit p2 --author "Jane Austin <jaustin@example.com>" &&
+ test "$(auth HEAD)" = "Jane Austin, jaustin@example.com"
+'
+
+test_expect_success 'Fail to set broken author' '
+ command_error stg edit p2 --author "No Mail Address" &&
+ test "$(auth HEAD)" = "Jane Austin, jaustin@example.com"
+'
+
+test_expect_success 'Set author name' '
+ stg edit p2 --authname "Jane Austen" &&
+ test "$(auth HEAD)" = "Jane Austen, jaustin@example.com"
+'
+
+test_expect_success 'Set author email' '
+ stg edit p2 --authemail "jausten@example.com" &&
+ test "$(auth HEAD)" = "Jane Austen, jausten@example.com"
+'
+
+test_expect_failure 'Set author date (RFC2822 format)' '
+ stg edit p2 --authdate "Wed, 10 Jul 2013 23:39:00 pm -0300" &&
+ test "$(date HEAD)" = "2013-07-10 23:39:00 -0300"
+'
+
+test_expect_failure 'Set author date (ISO 8601 format)' '
+ stg edit p2 --authdate "2013-01-28 22:30:00 -0300" &&
+ test "$(date HEAD)" = "2013-01-28 22:30:00 -0300"
+'
+
+test_expect_failure 'Fail to set invalid author date' '
+ command_error stg edit p2 --authdate "28 Jan 1813" &&
+ test "$(date HEAD)" = "2013-01-28 22:30:00 -0300"
+'
+
+test_done
^ permalink raw reply related
* [StGit PATCH 1/2] Test for specific exit code
From: Karl Hasselström @ 2008-07-08 4:03 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20080708035750.23134.75833.stgit@yoghurt>
When a command is supposed to fail in a test, test for the exact error
code we're expecting, not just that it's non-zero. This makes sure
e.g. that a command that's supposed to fail doesn't do so with an
unhandled exception.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
t/README | 6 +++---
t/t0001-subdir-branches.sh | 12 ++++++------
t/t0002-status.sh | 2 +-
t/t1000-branch-create.sh | 8 ++++----
t/t1001-branch-rename.sh | 2 +-
t/t1002-branch-clone.sh | 2 +-
t/t1200-push-modified.sh | 2 +-
t/t1202-push-undo.sh | 4 ++--
t/t1203-push-conflict.sh | 4 ++--
t/t1205-push-subdir.sh | 4 ++--
t/t1301-repair.sh | 2 +-
t/t1302-repair-interop.sh | 4 ++--
t/t1400-patch-history.sh | 2 +-
t/t1501-sink.sh | 2 +-
t/t1600-delete-one.sh | 4 ++--
t/t1601-delete-many.sh | 2 +-
t/t2000-sync.sh | 4 ++--
t/t2101-pull-policy-pull.sh | 2 +-
t/t2200-rebase.sh | 2 +-
t/t2500-clean.sh | 2 +-
t/t2900-rename.sh | 8 ++++----
t/t3000-dirty-merge.sh | 2 +-
t/t4000-upgrade.sh | 2 +-
t/test-lib.sh | 24 ++++++++++++++++++++++++
24 files changed, 66 insertions(+), 42 deletions(-)
diff --git a/t/README b/t/README
index 77f0b6c..757f810 100644
--- a/t/README
+++ b/t/README
@@ -163,9 +163,9 @@ library for your script to use.
yields success, test is considered a failure.
This should _not_ be used for tests that succeed when their
- commands fail -- use test_expect_success and shell negation (!) for
- that. test_expect_failure is for cases when a test is known to be
- broken.
+ commands fail -- use test_expect_success and one of general_error,
+ command_error, and conflict for that. test_expect_failure is for
+ cases when a test is known to be broken.
- test_debug <script>
diff --git a/t/t0001-subdir-branches.sh b/t/t0001-subdir-branches.sh
index 0eed3a4..69c11a3 100755
--- a/t/t0001-subdir-branches.sh
+++ b/t/t0001-subdir-branches.sh
@@ -39,9 +39,9 @@ test_expect_success 'Try new form of id with slashy branch' \
stg id foo@x/y/z//top'
test_expect_success 'Try old id with slashy branch' '
- ! stg id foo/ &&
- ! stg id foo/top &&
- ! stg id foo@x/y/z/top
+ command_error stg id foo/ &&
+ command_error stg id foo/top &&
+ command_error stg id foo@x/y/z/top
'
test_expect_success 'Create patch in slashy branch' \
@@ -51,11 +51,11 @@ test_expect_success 'Create patch in slashy branch' \
test_expect_success 'Rename branches' \
'stg branch --rename master goo/gaa &&
- ! git show-ref --verify --quiet refs/heads/master &&
+ must_fail git show-ref --verify --quiet refs/heads/master &&
stg branch --rename goo/gaa x1/x2/x3/x4 &&
- ! git show-ref --verify --quiet refs/heads/goo/gaa &&
+ must_fail git show-ref --verify --quiet refs/heads/goo/gaa &&
stg branch --rename x1/x2/x3/x4 servant &&
- ! git show-ref --verify --quiet refs/heads/x1/x2/x3/x4
+ must_fail git show-ref --verify --quiet refs/heads/x1/x2/x3/x4
'
test_done
diff --git a/t/t0002-status.sh b/t/t0002-status.sh
index 4364709..d9cb2cc 100755
--- a/t/t0002-status.sh
+++ b/t/t0002-status.sh
@@ -114,7 +114,7 @@ A fie
C foo/bar
EOF
test_expect_success 'Status after conflicting push' '
- ! stg push &&
+ conflict_old stg push &&
stg status > output.txt &&
test_cmp expected.txt output.txt
'
diff --git a/t/t1000-branch-create.sh b/t/t1000-branch-create.sh
index 5a097a4..3fff3ee 100755
--- a/t/t1000-branch-create.sh
+++ b/t/t1000-branch-create.sh
@@ -26,7 +26,7 @@ test_expect_success \
test_expect_success \
'Try to create an stgit branch with a spurious patches/ entry' '
- ! stg branch -c foo1
+ command_error stg branch -c foo1
'
test_expect_success \
@@ -43,7 +43,7 @@ test_expect_success \
test_expect_success \
'Try to create an stgit branch with an existing git branch by that name' '
- ! stg branch -c foo2
+ command_error stg branch -c foo2
'
test_expect_success \
@@ -58,7 +58,7 @@ test_expect_success \
test_expect_success \
'Create an invalid refs/heads/ entry' '
touch .git/refs/heads/foo3 &&
- ! stg branch -c foo3
+ command_error stg branch -c foo3
'
test_expect_failure \
@@ -87,7 +87,7 @@ test_expect_success \
test_expect_success \
'Create branch down the stack, behind the conflict caused by the generated file' '
- ! stg branch --create foo4 master^
+ command_error stg branch --create foo4 master^
'
test_expect_success \
diff --git a/t/t1001-branch-rename.sh b/t/t1001-branch-rename.sh
index dd12132..d5d3aef 100755
--- a/t/t1001-branch-rename.sh
+++ b/t/t1001-branch-rename.sh
@@ -19,7 +19,7 @@ test_expect_success \
test_expect_success \
'Rename the current stgit branch' \
- '! stg branch -r foo bar
+ 'command_error stg branch -r foo bar
'
test_expect_success \
diff --git a/t/t1002-branch-clone.sh b/t/t1002-branch-clone.sh
index b0087e9..19bdc45 100755
--- a/t/t1002-branch-clone.sh
+++ b/t/t1002-branch-clone.sh
@@ -21,7 +21,7 @@ test_expect_success \
test_expect_success \
'Try to create a patch in a GIT branch' \
'
- ! stg new p0 -m "p0"
+ command_error stg new p0 -m "p0"
'
test_expect_success \
diff --git a/t/t1200-push-modified.sh b/t/t1200-push-modified.sh
index ba4f70c..6ebd0a1 100755
--- a/t/t1200-push-modified.sh
+++ b/t/t1200-push-modified.sh
@@ -49,7 +49,7 @@ test_expect_success \
test_expect_success \
'Attempt to push the first of those patches without --merged' \
- "(cd bar && ! stg push
+ "(cd bar && conflict_old stg push
)
"
diff --git a/t/t1202-push-undo.sh b/t/t1202-push-undo.sh
index b602643..544fe8d 100755
--- a/t/t1202-push-undo.sh
+++ b/t/t1202-push-undo.sh
@@ -43,7 +43,7 @@ test_expect_success \
test_expect_success \
'Push the second patch with conflict' \
'
- ! stg push bar
+ conflict_old stg push bar
'
test_expect_success \
@@ -55,7 +55,7 @@ test_expect_success \
test_expect_success \
'Check the push after undo fails as well' \
'
- ! stg push bar
+ conflict_old stg push bar
'
test_expect_success \
diff --git a/t/t1203-push-conflict.sh b/t/t1203-push-conflict.sh
index 72bd49f..96fee15 100755
--- a/t/t1203-push-conflict.sh
+++ b/t/t1203-push-conflict.sh
@@ -38,7 +38,7 @@ test_expect_success \
test_expect_success \
'Push the first patch with conflict' \
'
- ! stg push foo
+ conflict_old stg push foo
'
test_expect_success \
@@ -56,7 +56,7 @@ test_expect_success \
test_expect_success \
'Check that pop will fail while there are unmerged conflicts' \
'
- ! stg pop
+ command_error stg pop
'
test_expect_success \
diff --git a/t/t1205-push-subdir.sh b/t/t1205-push-subdir.sh
index 175d36d..945eb74 100755
--- a/t/t1205-push-subdir.sh
+++ b/t/t1205-push-subdir.sh
@@ -47,7 +47,7 @@ test_expect_success 'Conflicting push from subdir' '
[ "$(echo $(cat x.txt))" = "x0" ] &&
[ "$(echo $(cat foo/y.txt))" = "y0" ] &&
cd foo &&
- ! stg push p2 &&
+ conflict_old stg push p2 &&
cd .. &&
[ "$(echo $(stg status --conflict))" = "foo/y.txt x.txt" ]
'
@@ -62,7 +62,7 @@ test_expect_success 'Conflicting add/unknown file in subdir' '
stg pop &&
mkdir -p d &&
echo bar > d/test &&
- ! stg push foo &&
+ command_error stg push foo &&
[ $(stg top) != "foo" ]
'
diff --git a/t/t1301-repair.sh b/t/t1301-repair.sh
index b555b93..33f8f6d 100755
--- a/t/t1301-repair.sh
+++ b/t/t1301-repair.sh
@@ -5,7 +5,7 @@ test_description='Test the repair command.'
test_expect_success \
'Repair in a non-initialized repository' \
- '! stg repair'
+ 'command_error stg repair'
test_expect_success \
'Initialize the StGIT repository' \
diff --git a/t/t1302-repair-interop.sh b/t/t1302-repair-interop.sh
index 82c5ed2..3ea48e7 100755
--- a/t/t1302-repair-interop.sh
+++ b/t/t1302-repair-interop.sh
@@ -27,7 +27,7 @@ test_expect_success 'Create five patches' '
test_expect_success 'Pop two patches with git-reset' '
git reset --hard HEAD~2 &&
- ! stg refresh &&
+ command_error stg refresh &&
stg repair &&
stg refresh &&
[ "$(echo $(stg applied))" = "p0 p1 p2" ] &&
@@ -42,7 +42,7 @@ test_expect_success 'Create a new patch' '
test_expect_success 'Go to an unapplied patch with with git-reset' '
git reset --hard $(stg id p3) &&
- ! stg refresh &&
+ command_error stg refresh &&
stg repair &&
stg refresh &&
[ "$(echo $(stg applied))" = "p0 p1 p2 p3" ] &&
diff --git a/t/t1400-patch-history.sh b/t/t1400-patch-history.sh
index a693e75..13cd1e3 100755
--- a/t/t1400-patch-history.sh
+++ b/t/t1400-patch-history.sh
@@ -82,7 +82,7 @@ test_expect_success \
echo bar > test && stg refresh &&
stg pop &&
echo foo > test && stg refresh &&
- ! stg push &&
+ conflict_old stg push &&
stg log --full | grep -q -e "^push(c) "
'
diff --git a/t/t1501-sink.sh b/t/t1501-sink.sh
index 6af45fe..ac9e25d 100755
--- a/t/t1501-sink.sh
+++ b/t/t1501-sink.sh
@@ -17,7 +17,7 @@ test_expect_success 'Initialize StGit stack' '
'
test_expect_success 'sink without applied patches' '
- ! stg sink
+ command_error stg sink
'
test_expect_success 'sink a specific patch without applied patches' '
diff --git a/t/t1600-delete-one.sh b/t/t1600-delete-one.sh
index c3451d8..b526a55 100755
--- a/t/t1600-delete-one.sh
+++ b/t/t1600-delete-one.sh
@@ -20,7 +20,7 @@ test_expect_success \
'Try to delete a non-existing patch' \
'
[ $(stg applied | wc -l) -eq 1 ] &&
- ! stg delete bar &&
+ command_error stg delete bar &&
[ $(stg applied | wc -l) -eq 1 ]
'
@@ -29,7 +29,7 @@ test_expect_success \
'
echo dirty >> foo.txt &&
[ $(stg applied | wc -l) -eq 1 ] &&
- ! stg delete foo &&
+ command_error stg delete foo &&
[ $(stg applied | wc -l) -eq 1 ] &&
git reset --hard
'
diff --git a/t/t1601-delete-many.sh b/t/t1601-delete-many.sh
index 30b0a1d..bc5364f 100755
--- a/t/t1601-delete-many.sh
+++ b/t/t1601-delete-many.sh
@@ -37,7 +37,7 @@ test_expect_success \
'
[ "$(echo $(stg applied))" = "p0 p1 p2" ] &&
[ "$(echo $(stg unapplied))" = "p5 p8 p9" ] &&
- ! stg delete p7 p8 p2 p0 &&
+ command_error stg delete p7 p8 p2 p0 &&
[ "$(echo $(stg applied))" = "p0 p1 p2" ] &&
[ "$(echo $(stg unapplied))" = "p5 p8 p9" ]
'
diff --git a/t/t2000-sync.sh b/t/t2000-sync.sh
index e489603..9852eb8 100755
--- a/t/t2000-sync.sh
+++ b/t/t2000-sync.sh
@@ -109,7 +109,7 @@ test_expect_success \
test_expect_success \
'Synchronise the first two patches with the master branch (to fail)' \
'
- ! stg sync -B master -a
+ conflict_old stg sync -B master -a
'
test_expect_success \
@@ -127,7 +127,7 @@ test_expect_success \
test_expect_success \
'Synchronise the third patch with the exported series (to fail)' \
'
- ! stg sync -s patches-master/series p3
+ conflict_old stg sync -s patches-master/series p3
'
test_expect_success \
diff --git a/t/t2101-pull-policy-pull.sh b/t/t2101-pull-policy-pull.sh
index ce4b5c8..777ccb5 100755
--- a/t/t2101-pull-policy-pull.sh
+++ b/t/t2101-pull-policy-pull.sh
@@ -43,7 +43,7 @@ test_expect_success \
'Rewind/rewrite upstream commit and pull it from clone, without --merged' \
'
(cd upstream && echo b >> file2 && stg refresh) &&
- (cd clone && ! stg pull)
+ (cd clone && conflict_old stg pull)
'
test_expect_success \
diff --git a/t/t2200-rebase.sh b/t/t2200-rebase.sh
index ec2a104..a6f43bc 100755
--- a/t/t2200-rebase.sh
+++ b/t/t2200-rebase.sh
@@ -34,7 +34,7 @@ test_expect_success \
test_expect_success \
'Attempt rebase to non-existing commit' \
'
- ! stg rebase not-a-ref
+ command_error stg rebase not-a-ref
'
test_expect_success \
diff --git a/t/t2500-clean.sh b/t/t2500-clean.sh
index ad8f892..abde9cf 100755
--- a/t/t2500-clean.sh
+++ b/t/t2500-clean.sh
@@ -32,7 +32,7 @@ test_expect_success 'Create a conflict' '
stg new p2 -m p2
echo quux > foo.txt &&
stg refresh &&
- ! stg push
+ conflict_old stg push
'
test_expect_success 'Make sure conflicting patches are preserved' '
diff --git a/t/t2900-rename.sh b/t/t2900-rename.sh
index 5f47f86..0e16d6e 100755
--- a/t/t2900-rename.sh
+++ b/t/t2900-rename.sh
@@ -13,7 +13,7 @@ Tests some parts of the stg rename command.'
stg init
test_expect_success 'Rename in empty' '
- ! stg rename foo
+ command_error stg rename foo
'
test_expect_success 'Rename single top-most' '
@@ -23,7 +23,7 @@ test_expect_success 'Rename single top-most' '
# bar
test_expect_success 'Rename non-existing' '
- ! stg rename neithersuchpatch norsuchpatch
+ command_error stg rename neithersuchpatch norsuchpatch
'
test_expect_success 'Rename with two arguments' '
@@ -33,11 +33,11 @@ test_expect_success 'Rename with two arguments' '
# foo,baz
test_expect_success 'Rename to existing name' '
- ! stg rename foo baz
+ command_error stg rename foo baz
'
test_expect_success 'Rename to same name' '
- ! stg rename foo foo
+ command_error stg rename foo foo
'
test_expect_success 'Rename top-most when others exist' '
diff --git a/t/t3000-dirty-merge.sh b/t/t3000-dirty-merge.sh
index d87bba1..4dd6da3 100755
--- a/t/t3000-dirty-merge.sh
+++ b/t/t3000-dirty-merge.sh
@@ -26,7 +26,7 @@ test_expect_success 'Push with dirty worktree' '
echo 4 > a &&
[ "$(echo $(stg applied))" = "p1" ] &&
[ "$(echo $(stg unapplied))" = "p2" ] &&
- ! stg goto p2 &&
+ conflict stg goto p2 &&
[ "$(echo $(stg applied))" = "p1" ] &&
[ "$(echo $(stg unapplied))" = "p2" ] &&
[ "$(echo $(cat a))" = "4" ]
diff --git a/t/t4000-upgrade.sh b/t/t4000-upgrade.sh
index 8a308fb..ea9bf0b 100755
--- a/t/t4000-upgrade.sh
+++ b/t/t4000-upgrade.sh
@@ -31,7 +31,7 @@ for ver in 0.12 0.8; do
test_expect_success \
"v$ver: Make sure the base ref is no longer there" '
- ! git show-ref --verify --quiet refs/bases/master
+ must_fail git show-ref --verify --quiet refs/bases/master
'
cd ..
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 95e322e..ad8da68 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -285,6 +285,30 @@ test_expect_code () {
echo >&3 ""
}
+# When running an StGit command that should exit with an error, use
+# these instead of testing for any non-zero exit code with !.
+exit_code () {
+ expected=$1
+ shift
+ "$@"
+ test $? -eq $expected
+}
+general_error () { exit_code 1 "$@" ; }
+command_error () { exit_code 2 "$@" ; }
+conflict () { exit_code 3 "$@" ; }
+
+# Old-infrastructure commands don't exit with the proper value on
+# conflicts. But we don't want half the tests to fail because of that,
+# so use this instead of "conflict" for them.
+conflict_old () { command_error "$@" ; }
+
+# Same thing, but for other commands that StGit where we just want to
+# make sure that they fail instead of crashing.
+must_fail () {
+ "$@"
+ test $? -gt 0 -a $? -le 129
+}
+
# test_cmp is a helper function to compare actual and expected output.
# You can use it like:
#
^ permalink raw reply related
* [StGit PATCH 0/2] Test improvements
From: Karl Hasselström @ 2008-07-08 4:03 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
First, a change across all the tests to make sure that stg always
exits with the correct exit code even when it's not zero. This lets us
detect the difference between a controlled failure and a crash.
Second, a rather comprehensive test fir "stg edit" -- we didn't test
it at all. Which can be seen in the number of failures in this test.
They are available in kha/safe. The patches in kha/experimental have
been rebased on top of these, resulting in a number of new test
failures that I've had to fix.
---
Karl Hasselström (2):
Test for "stg edit"
Test for specific exit code
t/README | 6 +
t/t0001-subdir-branches.sh | 12 +--
t/t0002-status.sh | 2
t/t1000-branch-create.sh | 8 +-
t/t1001-branch-rename.sh | 2
t/t1002-branch-clone.sh | 2
t/t1200-push-modified.sh | 2
t/t1202-push-undo.sh | 4 -
t/t1203-push-conflict.sh | 4 -
t/t1205-push-subdir.sh | 4 -
t/t1301-repair.sh | 2
t/t1302-repair-interop.sh | 4 -
t/t1400-patch-history.sh | 2
t/t1501-sink.sh | 2
t/t1600-delete-one.sh | 4 -
t/t1601-delete-many.sh | 2
t/t2000-sync.sh | 4 -
t/t2101-pull-policy-pull.sh | 2
t/t2200-rebase.sh | 2
t/t2500-clean.sh | 2
t/t2900-rename.sh | 8 +-
t/t3000-dirty-merge.sh | 2
t/t3300-edit.sh | 205 +++++++++++++++++++++++++++++++++++++++++++
t/t4000-upgrade.sh | 2
t/test-lib.sh | 24 +++++
25 files changed, 271 insertions(+), 42 deletions(-)
create mode 100755 t/t3300-edit.sh
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* Re: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Jay Soffian @ 2008-07-08 3:28 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git List, Brian Gernhardt
In-Reply-To: <7vod591hlp.fsf@gitster.siamese.dyndns.org>
On Mon, Jul 7, 2008 at 5:58 PM, Junio C Hamano <gitster@pobox.com> wrote:
> +HEAD names the commit your changes in the working tree is based on.
How about:
"HEAD names the commit your working tree is based on. This is the tip
of the checked out branch, unless HEAD is detached. (HEAD is said to
be detached if a commit is checked out which is not the tip of any
branch.)"
> +FETCH_HEAD records the branch you fetched from a remote repository
> +with your last 'git-fetch' invocation.
consistency w/above: s/records/names/
> +ORIG_HEAD is created by commands that moves your HEAD in a drastic
> +way, to record the position of the HEAD before their operation, so that
> +you can change the tip of the branch back to the state before you ran
> +them easily.
s/moves/move/; s/can change/can easily change/; s/them easily./them./;
But maybe this reads better:
ORIG_HEAD is created by commands that move HEAD in a drastic way to
record the position of HEAD before their operation, so that the branch
can easily be reset back to its prior state.
> +MERGE_HEAD records the commit(s) you are merging into your branch
> +when you run 'git-merge'.
So it's the "<remote>" arg or args mentioned in the git-merge man page?
j.
^ permalink raw reply
* Re: [PATCH] fix "git-submodule add a/b/c/repository"
From: Mark Levedahl @ 2008-07-08 3:26 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Sylvain Joyeux, Lars Hjemli, Ping Yin, git
In-Reply-To: <7v7ibxxfje.fsf@gitster.siamese.dyndns.org>
Junio C Hamano wrote:
> I'd like to hear clarifications on two counts, please?
> (1) If Sylvain wanted to have that appear at dir0/dir1/init not init,
> would it have been sufficient to give that path twice (once for
> <repository> and another for <path> parameter) to make things work as
> expected?
>
git-submodule really requires two arguments:
$ git submodule add <URL> <relative-path-to-module-in-tree>
and supports two modes:
1) relative-path exists and is a valid repo: just add the module, it was
created in tree, the user is expected to eventually push this to the
given URL so other users will get this as normal. This exists to
simplify the process of creating a repo to begin with.
2) relative-path doesn't exist: clone from the URL. This is the normal use.
submodule supports adding a module in one of two ways:
So,
$ git submodule add dir0/dir1/init dir0/dir1/init
will add the repo, but also makes the repo its own origin. I don't think
this makes sense.
> (2) Is it generally considered a sane use case to specify an existing
> repository inside the working tree of a superproject as a submodule
> using "git submodule add" like Sylvain's example did?
>
> I would have understood if the command were "git add dir0/dir1/init",
> but I have this vague recolleciton that "git submodule add" is about
> telling our repository about a submodule that comes from _outside_.
>
>
>
Adding an existing in-tree repo, ala
$ git submodule add <intended-URL> <path>
is there to ease the initial creation of a submodule. It can be created
and registered in-tree, and later pushed to the server. This is sane,
but is not the normal usage (makes sense only on creation).
Mark
^ permalink raw reply
* Re: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Brian Gernhardt @ 2008-07-08 3:24 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git List
In-Reply-To: <7vod591hlp.fsf@gitster.siamese.dyndns.org>
On Jul 7, 2008, at 5:58 PM, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
> And to answer your "git rebase --onto this from that-branch"
> question, I
> think ORIG_HEAD should record the tip of that-branch before rebase
> takes
> place, not the commit you happened to be at before running it.
> Switching
> branch to that-branch is not the drastic and unforseeable part. The
> drastic and unforseeable change is rebasing and seeing that the
> rebased
> result does not work with the new upstream `from`, and the user
> would want
> to have a way to quickly rewind the tip of the branch back to the
> state
> before the rebase. The new paragraph added by this patch should
> hopefully
> make this reasoning more clear.
I just wanted to make sure there was a clear reasoning and to see if
someone could word it clearly, as I was getting a little cross-eyed.
> -- >8 --
> Documentation: update sections on naming revisions and revision ranges
>
> Various *_HEAD pseudo refs were not documented in any central place.
> Especially since we may be teaching rebase and am to record ORIG_HEAD,
> it would be a good time to do so.
My only objection is to the "may". ;-)
Also, perhaps we should either list the commands that set ORIG_HEAD,
or add a note to that effect in their manpages. I'll see what wording
I can come up with, unless you (or someone else) gets to it first of
course.
> While at it, reword the explanation on r1..r2 notation to reduce
> confusion.
Looks good.
~~ Brian
^ permalink raw reply
* What's cooking in git.git (topics)
From: Junio C Hamano @ 2008-07-08 2:46 UTC (permalink / raw)
To: git
In-Reply-To: <7vlk0ffhw3.fsf@gitster.siamese.dyndns.org>
Here are the topics that have been cooking. Commits prefixed
with '-' are only in 'pu' while commits prefixed with '+' are
in 'next'.
The topics list the commits in reverse chronological order. The topics
meant to be applied to the maintenance series have "maint-" in their
names.
It already is beginning to become clear what 1.6.0 will look like. What's
already in 'next' all are well intentioned (I do not guarantee they are
already bug-free --- that is what cooking them in 'next' is for) and are
good set of feature enhancements. Bigger changes will be:
* Port for MinGW.
* With the default Makefile settings, most of the programs will be
installed outside your $PATH, except for "git", "gitk", "git-gui" and
some server side programs that need to be accessible for technical
reasons. Invoking a git subcommand as "git-xyzzy" from the command
line has been deprecated since early 2006 (and officially announced in
1.5.4 release notes); use of them from your scripts after adding
output from "git --exec-path" to the $PATH will still be supported in
1.6.0, but users are again strongly encouraged to adjust their
scripts to use "git xyzzy" form, as we will stop installing
"git-xyzzy" hardlinks for built-in commands in later releases.
* git-merge will be rewritten in C.
* default pack and idx versions will be updated as scheduled for some
time ago.
* GIT_CONFIG, which was only documented as affecting "git config", but
actually affected all git commands, now only affects "git config".
GIT_LOCAL_CONFIG, also only documented as affecting "git config" and
not different from GIT_CONFIG in a useful way, is removed.
----------------------------------------------------------------
[New Topics]
* jc/rebase-orig-head (Mon Jul 7 00:16:38 2008 -0700) 1 commit
+ Teach "am" and "rebase" to mark the original position with
ORIG_HEAD
* sb/sequencer (Tue Jul 1 04:38:34 2008 +0200) 4 commits
. Migrate git-am to use git-sequencer
. Add git-sequencer test suite (t3350)
. Add git-sequencer prototype documentation
. Add git-sequencer shell prototype
* js/pick-root (Fri Jul 4 16:19:52 2008 +0100) 1 commit
+ Allow cherry-picking root commits
* ab/bundle (Sat Jul 5 17:26:40 2008 -0400) 1 commit
+ Teach git-bundle to read revision arguments from stdin like git-
rev-list.
----------------------------------------------------------------
[Will merge to master soon]
* js/apply-root (Sun Jul 6 18:36:01 2008 -0700) 3 commits
+ git-apply --directory: make --root more similar to GNU diff
+ apply --root: thinkofix.
+ Teach "git apply" to prepend a prefix with "--root=<root>"
* jc/reflog-expire (Sat Jun 28 22:24:49 2008 -0700) 2 commits
+ Make default expiration period of reflog used for stash infinite
+ Per-ref reflog expiry configuration
As 1.6.0 will be a good time to make backward incompatible changes, the
tip commit makes the default expiry period of stash 'never', unless you
configure them to expire explicitly using gc.refs/stash.* variables.
Needs consensus, but I am guessing that enough people would want stash
that does not expire.
* jk/pager-config (Thu Jul 3 07:46:57 2008 -0400) 1 commit
+ Allow per-command pager config
----------------------------------------------------------------
[Actively Cooking]
* sg/stash-k-i (Fri Jun 27 16:37:15 2008 +0200) 1 commit
+ stash: introduce 'stash save --keep-index' option
One weakness of our "partial commit" workflow support used to be that the
user can incrementally build what is to be committed in the index but that
state cannot be tested as a whole in the working tree. This allows you to
temporarily stash the remaining changes in the working tree so that the
index state before running "stash save --keep-index" can be seen in the
working tree to be tested and then committed.
* am/stash-branch (Mon Jul 7 02:50:10 2008 +0530) 2 commits
+ Add a test for "git stash branch"
+ Implement "git stash branch <newbranch> <stash>"
Creates a new branch out of the stashed state, after returning from the
interrupt that forced you to create the stash in the first place.
* tr/add-i-e (Thu Jul 3 00:00:00 2008 +0200) 3 commits
+ git-add--interactive: manual hunk editing mode
+ git-add--interactive: remove hunk coalescing
+ git-add--interactive: replace hunk recounting with apply --recount
Adds 'e/dit' action to interactive add command.
* jc/report-tracking (Sun Jul 6 02:54:56 2008 -0700) 5 commits
+ branch -r -v: do not spit out garbage
+ stat_tracking_info(): clear object flags used during counting
+ git-branch -v: show the remote tracking statistics
+ git-status: show the remote tracking statistics
+ Refactor "tracking statistics" code used by "git checkout"
Makes the "your branch is ahead of the tracked one by N commits" logic and
messages available to other commands; status and branch are updated.
* jc/merge-theirs (Mon Jun 30 22:18:57 2008 -0700) 5 commits
+ Make "subtree" part more orthogonal to the rest of merge-
recursive.
+ Teach git-pull to pass -X<option> to git-merge
+ Teach git-merge to pass -X<option> to the backend strategy module
+ git-merge-recursive-{ours,theirs}
+ git-merge-file --ours, --theirs
Punting a merge by discarding your own work in conflicting parts but still
salvaging the parts that are cleanly automerged. It is likely that this
will result in nonsense mishmash, but somehow often people want this, so
here they are. The interface to the backends is updated so that you can
say "git merge -Xours -Xsubtree=foo/bar/baz -s recursive other" now.
The -X<option> part may change, Dscho mentions that a single-letter -X
that take stuck option is against syntax rules, and I think he's right.
This is more "because we can", not "because we need to".
* mv/merge-in-c (Mon Jul 7 19:24:20 2008 +0200) 15 commits
- Build in merge
+ Fix t7601-merge-pull-config.sh on AIX
+ git-commit-tree: make it usable from other builtins
+ Add new test case to ensure git-merge prepends the custom merge
message
+ Add new test case to ensure git-merge reduces octopus parents when
possible
+ Introduce reduce_heads()
+ Introduce get_merge_bases_many()
+ Add new test to ensure git-merge handles more than 25 refs.
+ Introduce get_octopus_merge_bases() in commit.c
+ git-fmt-merge-msg: make it usable from other builtins
+ Move read_cache_unmerged() to read-cache.c
+ Add new test to ensure git-merge handles pull.twohead and
pull.octopus
+ Move parse-options's skip_prefix() to git-compat-util.h
+ Move commit_list_count() to commit.c
+ Move split_cmdline() to alias.c
----------------------------------------------------------------
[Graduated to "master"]
* js/import-zip (Mon Jun 30 19:50:44 2008 +0100) 1 commit
+ Add another fast-import example, this time for .zip files
* db/no-git-config (Mon Jun 30 03:37:47 2008 -0400) 1 commit
+ Only use GIT_CONFIG in "git config", not other programs
* dr/ceiling (Mon May 19 23:49:34 2008 -0700) 4 commits
+ Eliminate an unnecessary chdir("..")
+ Add support for GIT_CEILING_DIRECTORIES
+ Fold test-absolute-path into test-path-utils
+ Implement normalize_absolute_path
* jc/rerere (Sun Jun 22 02:04:31 2008 -0700) 5 commits
+ rerere.autoupdate
+ t4200: fix rerere test
+ rerere: remove dubious "tail_optimization"
+ git-rerere: detect unparsable conflicts
+ rerere: rerere_created_at() and has_resolution() abstraction
A new configuration will allow paths that have been resolved cleanly by
rerere to be updated in the index automatically.
* js/maint-daemon-syslog (Thu Jul 3 16:27:24 2008 +0100) 1 commit
+ git daemon: avoid calling syslog() from a signal handler
Meant for 'maint' as well.
----------------------------------------------------------------
[On Hold]
* sg/merge-options (Sun Apr 6 03:23:47 2008 +0200) 1 commit
+ merge: remove deprecated summary and diffstat options and config
variables
This was previously in "will be in master soon" category, but it turns out
that the synonyms to the ones this one deletes are fairly new invention
that happend in 1.5.6 timeframe, and we cannot do this just yet. Perhaps
in 1.7.0.
* jc/dashless (Thu Jun 26 16:43:34 2008 -0700) 2 commits
+ Revert "Make clients ask for "git program" over ssh and local
transport"
+ Make clients ask for "git program" over ssh and local transport
This is the "botched" one. Will be resurrected during 1.7.0 or 1.8.0
timeframe.
* jk/renamelimit (Sat May 3 13:58:42 2008 -0700) 1 commit
- diff: enable "too large a rename" warning when -M/-C is explicitly
asked for
This would be the right thing to do for command line use, but gitk will be
hit due to tcl/tk's limitation, so I am holding this back for now.
----------------------------------------------------------------
[Stalled/Needs more work]
* jc/grafts (Wed Jul 2 17:14:12 2008 -0700) 1 commit
- [BROKEN wrt shallow clones] Ignore graft during object transfer
Cloning or fetching from a repository from grafts did not send objects
that are hidden by grafts, but the commits in the resulting repository do
need these to pass fsck. This fixes object transfer to ignore grafts.
Another fix is needed to git-prune so that it ignores grafts but treats
commits that are mentioned in grafts as reachable.
* ph/parseopt-step-blame (Tue Jun 24 11:12:12 2008 +0200) 7 commits
- Migrate git-blame to parse-option partially.
+ parse-opt: add PARSE_OPT_KEEP_ARGV0 parser option.
+ parse-opt: fake short strings for callers to believe in.
+ parse-opt: do not print errors on unknown options, return -2
intead.
+ parse-opt: create parse_options_step.
+ parse-opt: Export a non NORETURN usage dumper.
+ parse-opt: have parse_options_{start,end}.
I recall Pierre said something about cleaning up the last one when he
finds time, but other than that vague recollection, I lost track of this
series. I am tempted to fork a few topics off of the penúltimo one to
convert a few more commands as examples and merge the result to 'next'.
* jc/blame (Wed Jun 4 22:58:40 2008 -0700) 7 commits
- blame: show "previous" information in --porcelain/--incremental
format
- git-blame: refactor code to emit "porcelain format" output
+ git-blame --reverse
+ builtin-blame.c: allow more than 16 parents
+ builtin-blame.c: move prepare_final() into a separate function.
+ rev-list --children
+ revision traversal: --children option
The blame that finds where each line in the original lines moved to. This
may help a GSoC project that wants to gather statistical overview of the
history. The final presentation may need tweaking (see the log message of
the commit ""git-blame --reverse" on the series).
The tip two commits are for peeling to see what's behind the blamed
commit, which we should be able to separate out into an independent topic
from the rest.
^ permalink raw reply
* What's in git.git (stable)
From: Junio C Hamano @ 2008-07-08 2:46 UTC (permalink / raw)
To: git
In-Reply-To: <7vr6a7fhwh.fsf@gitster.siamese.dyndns.org>
* The 'maint' branch has these fixes since 1.5.6.2.
Alex Riesen (1):
git-clone: remove leftover debugging fprintf().
Brian Hetro (5):
builtin-log.c: Use 'git_config_string' to get 'format.subjectprefix' and
'format.suffix'
convert.c: Use 'git_config_string' to get 'smudge' and 'clean'
diff.c: Use 'git_config_string' to get 'diff.external'
http.c: Use 'git_config_string' to clean up SSL config.
builtin-commit.c: Use 'git_config_string' to get 'commit.template'
Christian Couder (1):
Fix "config_error_nonbool" used with value instead of key
Gerrit Pape (1):
git-svn.perl: workaround assertions in svn library 1.5.0
Junio C Hamano (3):
attribute documentation: keep EXAMPLE at end
clone -q: honor "quiet" option over native transports.
mailinfo: feed the correct line length to decode_transfer_encoding()
Matthew Ogilvie (1):
Documentation cvs: Clarify when a bare repository is needed
Mikael Magnusson (1):
Fix grammar in git-rev-parse(1).
Nikolaus Schulz (1):
Documentation: be precise about which date --pretty uses
* The 'master' branch has these since the last announcement
in addition to the above.
Abhijit Menon-Sen (2):
git-gui: Move on to the next filename after staging/unstaging a change
git-gui: Don't select the wrong file if the last listed file is staged.
Daniel Barkalow (1):
Only use GIT_CONFIG in "git config", not other programs
David Reiss (4):
Implement normalize_absolute_path
Fold test-absolute-path into test-path-utils
Add support for GIT_CEILING_DIRECTORIES
Eliminate an unnecessary chdir("..")
Dmitry Potapov (1):
completion.bash: add 'skip' and 'run' to git-bisect
Jakub Narebski (1):
gitweb: Describe projects_index format in more detail
Johannes Schindelin (3):
Add another fast-import example, this time for .zip files
git daemon: avoid calling syslog() from a signal handler
run_command(): respect GIT_TRACE
Johannes Sixt (1):
git-gui: Implement "Stage/Unstage Line"
Junio C Hamano (6):
rerere: rerere_created_at() and has_resolution() abstraction
git-rerere: detect unparsable conflicts
rerere: remove dubious "tail_optimization"
t4200: fix rerere test
rerere.autoupdate
Update draft release notes for 1.6.0
Richard Quirk (1):
git-gui: Fix accidental staged state toggle when clicking top pixel row
^ permalink raw reply
* Re: [PATCH] fix "git-submodule add a/b/c/repository"
From: Junio C Hamano @ 2008-07-08 2:42 UTC (permalink / raw)
To: Mark Levedahl; +Cc: Sylvain Joyeux, Lars Hjemli, Ping Yin, git
In-Reply-To: <4872CF86.5050702@gmail.com>
Mark Levedahl <mlevedahl@gmail.com> writes:
> Sylvain Joyeux wrote:
>>
>> Redo the prep work, the clone and now
>>
>> git submodule add dir0/dir1/init
>>
>> (i.e. don't expect dir0/dir1/init to be the clone of ./init, that was just a
>> shortcut for the test. Expect it to be a clone of "something, somewhere")
>>
>>
> Per the man-page,
> git submodule [--quiet] add [-b branch] [--] <repository> [<path>]
>
> which means, that the *repository* url is mandatory, the path is
> optional. What you specifically asked git-submodule to do was to
> *clone* from dir0/dir1/init, and because you gave no path to put the
> submodule in, git-submodule deduced the name as "init", and cloned to
> there.
I'd like to hear clarifications on two counts, please?
(1) If Sylvain wanted to have that appear at dir0/dir1/init not init,
would it have been sufficient to give that path twice (once for
<repository> and another for <path> parameter) to make things work as
expected?
(2) Is it generally considered a sane use case to specify an existing
repository inside the working tree of a superproject as a submodule
using "git submodule add" like Sylvain's example did?
I would have understood if the command were "git add dir0/dir1/init",
but I have this vague recolleciton that "git submodule add" is about
telling our repository about a submodule that comes from _outside_.
^ permalink raw reply
* [PATCH] install-doc-quick - use git --exec-path to find git-sh-setup
From: Mark Levedahl @ 2008-07-08 2:37 UTC (permalink / raw)
To: git; +Cc: Mark Levedahl
This is needed as git-sh-setup is no longer in the path.
Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
Documentation/install-doc-quick.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
index 5433cf8..35f4408 100755
--- a/Documentation/install-doc-quick.sh
+++ b/Documentation/install-doc-quick.sh
@@ -6,7 +6,7 @@ head="$1"
mandir="$2"
SUBDIRECTORY_OK=t
USAGE='<refname> <target directory>'
-. git-sh-setup
+. "$(git --exec-path)"/git-sh-setup
cd_to_toplevel
test -z "$mandir" && usage
--
1.5.6.2.271.g73ad8
^ permalink raw reply related
* [PATCH] git-submodule - register module url if adding in place
From: Mark Levedahl @ 2008-07-08 2:37 UTC (permalink / raw)
To: git; +Cc: Mark Levedahl
When adding a new submodule in place, meaning the user created the
submodule as a git repo in the tree first, we don't go through
git submodule init to register the module. Thus, the url is
not stored in .git/config, and no git operation will ever do so. In
this case, assume the url the user supplies to git add is the one
that should be registered, and do so.
Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
git-submodule.sh | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/git-submodule.sh b/git-submodule.sh
index 099a7d7..7525922 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -173,6 +173,16 @@ cmd_add()
else
die "'$path' already exists and is not a valid git repo"
fi
+
+ case "$repo" in
+ ./*|../*)
+ url=$(resolve_relative_url "$repo") || exit
+ ;;
+ *)
+ url="$repo"
+ ;;
+ esac
+ git config submodule."$path".url "$url"
else
case "$repo" in
./*|../*)
--
1.5.6.2.271.g73ad8
^ permalink raw reply related
* [PATCH] git-submodule - Fix bugs in adding an existing repo as a module
From: Mark Levedahl @ 2008-07-08 2:36 UTC (permalink / raw)
To: git; +Cc: Mark Levedahl
git-submodule add would trip if path to the submodule included a space,
or if its .git was a gitdir: link to a GIT_DIR kept elsewhere. Fix both.
Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
git-submodule.sh | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/git-submodule.sh b/git-submodule.sh
index 3eb78cc..099a7d7 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -167,8 +167,7 @@ cmd_add()
# perhaps the path exists and is already a git repo, else clone it
if test -e "$path"
then
- if test -d "$path/.git" &&
- test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+ if test -d "$path"/.git -o -f "$path"/.git
then
echo "Adding existing repo at '$path' to the index"
else
--
1.5.6.2.271.g73ad8
^ permalink raw reply related
* Re: [PATCH] fix "git-submodule add a/b/c/repository"
From: Mark Levedahl @ 2008-07-08 2:23 UTC (permalink / raw)
To: Sylvain Joyeux; +Cc: Junio C Hamano, Lars Hjemli, Ping Yin, git
In-Reply-To: <20080707063424.GB5506@jhaampe.org>
Sylvain Joyeux wrote:
>
> Redo the prep work, the clone and now
>
> git submodule add dir0/dir1/init
>
> (i.e. don't expect dir0/dir1/init to be the clone of ./init, that was just a
> shortcut for the test. Expect it to be a clone of "something, somewhere")
>
>
Per the man-page,
git submodule [--quiet] add [-b branch] [--] <repository> [<path>]
which means, that the *repository* url is mandatory, the path is
optional. What you specifically asked git-submodule to do was to *clone*
from dir0/dir1/init, and because you gave no path to put the submodule
in, git-submodule deduced the name as "init", and cloned to there.
Mark
^ permalink raw reply
* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Stephan Beyer @ 2008-07-08 1:47 UTC (permalink / raw)
To: Junio C Hamano
Cc: Jakub Narebski, git, Sam Vilain, Joshua Roys, Sverre Rabbelier,
Sverre Rabbelier, David Symonds, Lea Wiemann, John Hawley,
Marek Zawirski, Shawn O. Pearce, Miklos Vajna,
Johannes Schindelin, Christian Couder, Daniel Barkalow
In-Reply-To: <7vmyktxjlq.fsf@gitster.siamese.dyndns.org>
Hi,
Junio C Hamano <gitster@pobox.com> wrote:
> Stephan Beyer <s-beyer@gmx.net> writes:
> > (Well, I think the
> > http://thread.gmane.org/gmane.comp.version-control.git/86985
> > thread died in the last days, but I hope some responses will come and
> > also that Junio's patch for cherry-picking root commits will be
> > included. I try to be patient...) ;-)
>
> Please don't be patient but actively re-review what you sent out.
>
> I _really_ wanted to merge the basic bits and rewrite of "am" at least to
> pu tonight, but I had to drop them after noticing that it does not seem to
> handle --rebasing at all (it parses to set $rebasing but after that where
> does that bit go?
Yes, you are right that am --rebasing is a no-op.
That option was a little mystery to me, because it seemed to do nothing
special, but I'll check again (bash-completion etc) and do appropriate
changes.
> About the "rewrite rebase to use sequencer" bits, because we've dropped
> the older rebase-i change, I do not want your series to depend on it.
Yes, I made the mistake that I started developing on "next" and I did
not expect that js/rebase-i-sequencer could be dropped. (I somehow
expected the opposite, that it would be migrated to master some day.)
Then I wanted to make it work on master, and thought this is simply
done by removing the "additional features" (-f and -p option using extended
todo list), but then I noticed that master also has a -p which works
entirely different and I wanted to see that stuff on the list before
July 1st and I had no idea what the status of js/rebase-i-sequencer is.
So I just tried and sent that last patch to the list in that way.
But now I have a clear statement.
So I'm going to send a new patchset (also with an EXAMPLES section in
the docs) to the list in a few days.
Big thanks for the feedback,
Stephan
--
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F
^ permalink raw reply
* Re: [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-08 1:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7vskulxk0o.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 1232 bytes --]
On Mon, Jul 07, 2008 at 06:05:43PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> > 1) A project has code in a repo, some documentation and po files.
> >
> > The first two can be merged with builtin strategies, the later probably
> > needs a custom merge driver.
>
> Per-path merge is probably better handled with custom ll-merge driver
> anyway. See gitattributes(5).
Ah, thanks. I did not know ll-merge supports custom drivers as well.
> It wasn't *me* ;-) who wanted to add these "flags".
>
> I think it does not matter what "my-strategy" does unless "-s my-strategy"
> (or pull.twohead) is explicitly given by the user, and at that time,
> DEFAULT_* options should not matter. It probably is Ok to allow fast
> forward and trivial merges to them. We'll see.
OK, so at first round I think we could avoid flags for custom
strategies.
For the error message, I think the output could be something like git
help --all, which splits commands based on being in
GIT_EXEC_PATH or somewhere else in PATH.
Also, currently (the shell version and the C one as well) we silently
ignore the config setting if it's set to semething invalid. What about
changing that to a similar error message as well?
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Josef 'Jeff' Sipek @ 2008-07-08 1:28 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Theodore Ts'o, git
In-Reply-To: <7viqvhxjhc.fsf@gitster.siamese.dyndns.org>
On Mon, Jul 07, 2008 at 06:17:19PM -0700, Junio C Hamano wrote:
> "Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:
>
> > A fresh clone (from master.kernel.org over ssh) gets me:
> >
> > $ git describe
> > v1.5.6.2-247-g9237122
> > $ git --version
> > git version 1.5.6.GIT
> > $ git --exec-path
> > /home/jeffpc/git-bin/bin
> >
> > You seem to have a different HEAD for the master branch than I do.
>
> Oh, my private 'master' almost always is ahead of the public one after
> work.
>
> But I do not have any new changes around this area lately. Promise ;-).
Heh.
Alright, mystery solved. I had a GIT_EXEC_PATH set. Removing it from my
bash_profile makes everything work just fine.
Sorry for the noise.
Jeff.
--
Penguin : Linux version 2.6.25.4 on an i386 machine (6135.77 BogoMips).
^ permalink raw reply
* [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-08 1:18 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7v1w25yz5o.fsf@gitster.siamese.dyndns.org>
Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Mon, Jul 07, 2008 at 05:53:23PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Here is what I meant to suggest; sorry for confusing you with an
> initial
> typo of having only one pointer in front of use_strategies "array of
> pointers". Applies on top of your previous round.
Funny enough, I just did almost the same before reading your mail,
except that I did not do the suggested strategy_lookup() ->
get_strategy() rename and such.
Here is a squashed patch. (Previous round + your patch.)
Makefile | 2 +-
builtin-merge.c | 1153 +++++++++++++++++++++++++
builtin.h | 1 +
git-merge.sh => contrib/examples/git-merge.sh | 0
git.c | 1 +
t/t7602-merge-octopus-many.sh | 2 +-
6 files changed, 1157 insertions(+), 2 deletions(-)
create mode 100644 builtin-merge.c
rename git-merge.sh => contrib/examples/git-merge.sh (100%)
diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
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
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
BUILTIN_OBJS += builtin-ls-tree.o
BUILTIN_OBJS += builtin-mailinfo.o
BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
BUILTIN_OBJS += builtin-merge-base.o
BUILTIN_OBJS += builtin-merge-file.o
BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..d6bd144
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1153 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL (1<<3)
+
+struct strategy {
+ const char *name;
+ unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+ "git-merge [options] <remote>...",
+ "git-merge [options] <msg> HEAD <remote>",
+ NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+static struct commit_list *remoteheads;
+static unsigned char head[20], stash[20];
+static struct strategy **use_strategies;
+static size_t use_strategies_nr, use_strategies_alloc;
+static const char *branch;
+
+static struct strategy all_strategy[] = {
+ { "recur", NO_TRIVIAL },
+ { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "octopus", DEFAULT_OCTOPUS },
+ { "resolve", 0 },
+ { "stupid", 0 },
+ { "ours", NO_FAST_FORWARD | NO_TRIVIAL },
+ { "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct strbuf *buf = opt->value;
+
+ if (unset)
+ strbuf_setlen(buf, 0);
+ else {
+ strbuf_addf(buf, "%s\n\n", arg);
+ have_message = 1;
+ }
+ return 0;
+}
+
+static struct strategy *get_strategy(const char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+ if (!strcmp(name, all_strategy[i].name))
+ return &all_strategy[i];
+ return NULL;
+}
+
+static void append_strategy(struct strategy *s)
+{
+ ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+ use_strategies[use_strategies_nr++] = s;
+}
+
+static int option_parse_strategy(const struct option *opt,
+ const char *name, int unset)
+{
+ int i;
+ struct strategy *s;
+
+ if (unset)
+ return 0;
+
+ s = get_strategy(name);
+
+ if (s)
+ append_strategy(s);
+ else {
+ struct strbuf err;
+ strbuf_init(&err, 0);
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+ strbuf_addf(&err, " %s", all_strategy[i].name);
+ fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+ fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+ exit(1);
+ }
+ return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+ const char *arg, int unset)
+{
+ show_diffstat = unset;
+ return 0;
+}
+
+static struct option builtin_merge_options[] = {
+ { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+ "do not show a diffstat at the end of the merge",
+ PARSE_OPT_NOARG, option_parse_n },
+ OPT_BOOLEAN(0, "stat", &show_diffstat,
+ "show a diffstat at the end of the merge"),
+ OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+ OPT_BOOLEAN(0, "log", &option_log,
+ "add list of one-line log to merge commit message"),
+ OPT_BOOLEAN(0, "squash", &squash,
+ "create a single commit instead of doing a merge"),
+ OPT_BOOLEAN(0, "commit", &option_commit,
+ "perform a commit if the merge succeeds (default)"),
+ OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+ "allow fast forward (default)"),
+ OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+ "merge strategy to use", option_parse_strategy),
+ OPT_CALLBACK('m', "message", &merge_msg, "message",
+ "message to be used for the merge commit (if any)",
+ option_parse_message),
+ OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+ unlink(git_path("MERGE_HEAD"));
+ unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+ int len;
+ struct child_process cp;
+ struct strbuf buffer = STRBUF_INIT;
+ const char *argv[] = {"stash", "create", NULL};
+
+ memset(&cp, 0, sizeof(cp));
+ cp.argv = argv;
+ cp.out = -1;
+ cp.git_cmd = 1;
+
+ if (start_command(&cp))
+ die("could not run stash.");
+ len = strbuf_read(&buffer, cp.out, 1024);
+ close(cp.out);
+
+ if (finish_command(&cp) || len < 0)
+ die("stash failed");
+ else if (!len)
+ return;
+ strbuf_setlen(&buffer, buffer.len-1);
+ if (get_sha1(buffer.buf, stash))
+ die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+ int i = 0;
+ const char *args[6];
+
+ args[i++] = "read-tree";
+ if (verbose)
+ args[i++] = "-v";
+ args[i++] = "--reset";
+ args[i++] = "-u";
+ args[i++] = sha1_to_hex(sha1);
+ args[i] = NULL;
+
+ if (run_command_v_opt(args, RUN_GIT_CMD))
+ die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+ struct strbuf sb;
+ const char *args[] = { "stash", "apply", NULL, NULL };
+
+ if (is_null_sha1(stash))
+ return;
+
+ reset_hard(head, 1);
+
+ strbuf_init(&sb, 0);
+ args[2] = sha1_to_hex(stash);
+
+ /*
+ * It is OK to ignore error here, for example when there was
+ * nothing to restore.
+ */
+ run_command_v_opt(args, RUN_GIT_CMD);
+
+ strbuf_release(&sb);
+ refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+ printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+ drop_save();
+}
+
+static void squash_message(void)
+{
+ struct rev_info rev;
+ struct commit *commit;
+ struct strbuf out;
+ struct commit_list *j;
+ int fd;
+
+ printf("Squash commit -- not updating HEAD\n");
+ fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die("Could not write to %s", git_path("SQUASH_MSG"));
+
+ init_revisions(&rev, NULL);
+ rev.ignore_merges = 1;
+ rev.commit_format = CMIT_FMT_MEDIUM;
+
+ commit = lookup_commit(head);
+ commit->object.flags |= UNINTERESTING;
+ add_pending_object(&rev, &commit->object, NULL);
+
+ for (j = remoteheads; j; j = j->next)
+ add_pending_object(&rev, &j->item->object, NULL);
+
+ setup_revisions(0, NULL, &rev, NULL);
+ if (prepare_revision_walk(&rev))
+ die("revision walk setup failed");
+
+ strbuf_init(&out, 0);
+ strbuf_addstr(&out, "Squashed commit of the following:\n");
+ while ((commit = get_revision(&rev)) != NULL) {
+ strbuf_addch(&out, '\n');
+ strbuf_addf(&out, "commit %s\n",
+ sha1_to_hex(commit->object.sha1));
+ pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+ NULL, NULL, rev.date_mode, 0);
+ }
+ write(fd, out.buf, out.len);
+ close(fd);
+ strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+ struct child_process hook;
+ const char *argv[3], *env[2];
+ char index[PATH_MAX];
+
+ argv[0] = git_path("hooks/%s", name);
+ if (access(argv[0], X_OK) < 0)
+ return 0;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+ env[0] = index;
+ env[1] = NULL;
+
+ if (squash)
+ argv[1] = "1";
+ else
+ argv[1] = "0";
+ argv[2] = NULL;
+
+ memset(&hook, 0, sizeof(hook));
+ hook.argv = argv;
+ hook.no_stdin = 1;
+ hook.stdout_to_stderr = 1;
+ hook.env = env;
+
+ return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+ struct strbuf reflog_message;
+
+ strbuf_init(&reflog_message, 0);
+ if (!msg)
+ strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+ else {
+ printf("%s\n", msg);
+ strbuf_addf(&reflog_message, "%s: %s",
+ getenv("GIT_REFLOG_ACTION"), msg);
+ }
+ if (squash) {
+ squash_message();
+ } else {
+ if (!merge_msg.len)
+ printf("No merge message -- not updating HEAD\n");
+ else {
+ const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+ update_ref(reflog_message.buf, "HEAD",
+ new_head, head, 0,
+ DIE_ON_ERR);
+ /*
+ * We ignore errors in 'gc --auto', since the
+ * user should see them.
+ */
+ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ }
+ }
+ if (new_head && show_diffstat) {
+ struct diff_options opts;
+ diff_setup(&opts);
+ opts.output_format |=
+ DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+ opts.detect_rename = DIFF_DETECT_RENAME;
+ if (diff_use_color_default > 0)
+ DIFF_OPT_SET(&opts, COLOR_DIFF);
+ if (diff_setup_done(&opts) < 0)
+ die("diff_setup_done failed");
+ diff_tree_sha1(head, new_head, "", &opts);
+ diffcore_std(&opts);
+ diff_flush(&opts);
+ }
+
+ /* Run a post-merge hook */
+ run_hook("post-merge");
+
+ strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+ struct object *remote_head;
+ unsigned char branch_head[20], buf_sha[20];
+ struct strbuf buf;
+ const char *ptr;
+ int len, early;
+
+ memset(branch_head, 0, sizeof(branch_head));
+ remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+ if (!remote_head)
+ die("'%s' does not point to a commit", remote);
+
+ strbuf_init(&buf, 0);
+ strbuf_addstr(&buf, "refs/heads/");
+ strbuf_addstr(&buf, remote);
+ resolve_ref(buf.buf, branch_head, 0, 0);
+
+ if (!hashcmp(remote_head->sha1, branch_head)) {
+ strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+ sha1_to_hex(branch_head), remote);
+ return;
+ }
+
+ /* See if remote matches <name>^^^.. or <name>~<number> */
+ for (len = 0, ptr = remote + strlen(remote);
+ remote < ptr && ptr[-1] == '^';
+ ptr--)
+ len++;
+ if (len)
+ early = 1;
+ else {
+ early = 0;
+ ptr = strrchr(remote, '~');
+ if (ptr) {
+ int seen_nonzero = 0;
+
+ len++; /* count ~ */
+ while (*++ptr && isdigit(*ptr)) {
+ seen_nonzero |= (*ptr != '0');
+ len++;
+ }
+ if (*ptr)
+ len = 0; /* not ...~<number> */
+ else if (seen_nonzero)
+ early = 1;
+ else if (len == 1)
+ early = 1; /* "name~" is "name~1"! */
+ }
+ }
+ if (len) {
+ struct strbuf truname = STRBUF_INIT;
+ strbuf_addstr(&truname, "refs/heads/");
+ strbuf_addstr(&truname, remote);
+ strbuf_setlen(&truname, len+11);
+ if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+ strbuf_addf(msg,
+ "%s\t\tbranch '%s'%s of .\n",
+ sha1_to_hex(remote_head->sha1),
+ truname.buf,
+ (early ? " (early part)" : ""));
+ return;
+ }
+ }
+
+ if (!strcmp(remote, "FETCH_HEAD") &&
+ !access(git_path("FETCH_HEAD"), R_OK)) {
+ FILE *fp;
+ struct strbuf line;
+ char *ptr;
+
+ strbuf_init(&line, 0);
+ fp = fopen(git_path("FETCH_HEAD"), "r");
+ if (!fp)
+ die("could not open %s for reading: %s",
+ git_path("FETCH_HEAD"), strerror(errno));
+ strbuf_getline(&line, fp, '\n');
+ fclose(fp);
+ ptr = strstr(line.buf, "\tnot-for-merge\t");
+ if (ptr)
+ strbuf_remove(&line, ptr-line.buf+1, 13);
+ strbuf_addbuf(msg, &line);
+ strbuf_release(&line);
+ return;
+ }
+ strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+ sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+ if (branch && !prefixcmp(k, "branch.") &&
+ !prefixcmp(k + 7, branch) &&
+ !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+ const char **argv;
+ int argc;
+ char *buf;
+
+ buf = xstrdup(v);
+ argc = split_cmdline(buf, &argv);
+ argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+ memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+ argc++;
+ parse_options(argc, argv, builtin_merge_options,
+ builtin_merge_usage, 0);
+ free(buf);
+ }
+
+ if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+ show_diffstat = git_config_bool(k, v);
+ else if (!strcmp(k, "pull.twohead"))
+ return git_config_string(&pull_twohead, k, v);
+ else if (!strcmp(k, "pull.octopus"))
+ return git_config_string(&pull_octopus, k, v);
+ return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+ unsigned char *one)
+{
+ int i, nr_trees = 0;
+ struct tree *trees[MAX_UNPACK_TREES];
+ struct tree_desc t[MAX_UNPACK_TREES];
+ struct unpack_trees_options opts;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 2;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.update = 1;
+ opts.verbose_update = 1;
+ opts.trivial_merges_only = 1;
+ opts.merge = 1;
+ trees[nr_trees] = parse_tree_indirect(common);
+ if (!trees[nr_trees++])
+ return -1;
+ trees[nr_trees] = parse_tree_indirect(head);
+ if (!trees[nr_trees++])
+ return -1;
+ trees[nr_trees] = parse_tree_indirect(one);
+ if (!trees[nr_trees++])
+ return -1;
+ opts.fn = threeway_merge;
+ cache_tree_free(&active_cache_tree);
+ for (i = 0; i < nr_trees; i++) {
+ parse_tree(trees[i]);
+ init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ }
+ if (unpack_trees(nr_trees, t, &opts))
+ return -1;
+ return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+ if (write_cache_as_tree(sha1, 0, NULL))
+ die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+ const char *head_arg)
+{
+ const char **args;
+ int i = 0, ret;
+ struct commit_list *j;
+ struct strbuf buf;
+
+ args = xmalloc((4 + commit_list_count(common) +
+ commit_list_count(remoteheads)) * sizeof(char *));
+ strbuf_init(&buf, 0);
+ strbuf_addf(&buf, "merge-%s", strategy);
+ args[i++] = buf.buf;
+ for (j = common; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remoteheads; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i] = NULL;
+ ret = run_command_v_opt(args, RUN_GIT_CMD);
+ strbuf_release(&buf);
+ i = 1;
+ for (j = common; j; j = j->next)
+ free((void *)args[i++]);
+ i += 2;
+ for (j = remoteheads; j; j = j->next)
+ free((void *)args[i++]);
+ free(args);
+ return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+ struct diff_options *opt, void *data)
+{
+ int *count = data;
+
+ (*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+ const struct index_state *state = &the_index;
+ int i, ret = 0;
+
+ for (i = 0; i < state->cache_nr; i++)
+ if (ce_stage(state->cache[i]))
+ ret++;
+
+ return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+ struct tree *trees[MAX_UNPACK_TREES];
+ struct unpack_trees_options opts;
+ struct tree_desc t[MAX_UNPACK_TREES];
+ int i, fd, nr_trees = 0;
+ struct dir_struct dir;
+ struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+ if (read_cache_unmerged())
+ die("you need to resolve your current index first");
+
+ fd = hold_locked_index(lock_file, 1);
+
+ memset(&trees, 0, sizeof(trees));
+ memset(&opts, 0, sizeof(opts));
+ memset(&t, 0, sizeof(t));
+ dir.show_ignored = 1;
+ dir.exclude_per_dir = ".gitignore";
+ opts.dir = &dir;
+
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.update = 1;
+ opts.verbose_update = 1;
+ opts.merge = 1;
+ opts.fn = twoway_merge;
+
+ trees[nr_trees] = parse_tree_indirect(head);
+ if (!trees[nr_trees++])
+ return -1;
+ trees[nr_trees] = parse_tree_indirect(remote);
+ if (!trees[nr_trees++])
+ return -1;
+ for (i = 0; i < nr_trees; i++) {
+ parse_tree(trees[i]);
+ init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ }
+ if (unpack_trees(nr_trees, t, &opts))
+ return -1;
+ if (write_cache(fd, active_cache, active_nr) ||
+ commit_locked_index(lock_file))
+ die("unable to write new index file");
+ return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+ int *nr, int *alloc)
+{
+ char *p, *q, *buf;
+
+ if (!string)
+ return;
+
+ buf = xstrdup(string);
+ q = buf;
+ for (;;) {
+ p = strchr(q, ' ');
+ if (!p) {
+ ALLOC_GROW(*list, *nr + 1, *alloc);
+ (*list)[(*nr)++].name = xstrdup(q);
+ free(buf);
+ return;
+ } else {
+ *p = '\0';
+ ALLOC_GROW(*list, *nr + 1, *alloc);
+ (*list)[(*nr)++].name = xstrdup(q);
+ q = ++p;
+ }
+ }
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+ struct strategy *list = NULL;
+ int list_alloc = 0, list_nr = 0, i;
+
+ memset(&list, 0, sizeof(list));
+ split_merge_strategies(string, &list, &list_nr, &list_alloc);
+ if (list != NULL) {
+ for (i = 0; i < list_nr; i++) {
+ struct strategy *s;
+
+ s = get_strategy(list[i].name);
+ if (s)
+ append_strategy(s);
+ }
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+ if (all_strategy[i].attr & attr)
+ append_strategy(&all_strategy[i]);
+
+}
+
+static int merge_trivial(void)
+{
+ unsigned char result_tree[20], result_commit[20];
+ struct commit_list parent;
+
+ write_tree_trivial(result_tree);
+ printf("Wonderful.\n");
+ parent.item = remoteheads->item;
+ parent.next = NULL;
+ commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+ finish(result_commit, "In-index merge");
+ drop_save();
+ return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+ unsigned char *result_tree,
+ const char *wt_strategy)
+{
+ struct commit_list *parents = NULL, *j;
+ struct strbuf buf = STRBUF_INIT;
+ unsigned char result_commit[20];
+
+ free_commit_list(common);
+ if (allow_fast_forward) {
+ parents = remoteheads;
+ commit_list_insert(lookup_commit(head), &parents);
+ parents = reduce_heads(parents);
+ } else {
+ struct commit_list **pptr = &parents;
+
+ pptr = &commit_list_insert(lookup_commit(head),
+ pptr)->next;
+ for (j = remoteheads; j; j = j->next)
+ pptr = &commit_list_insert(j->item, pptr)->next;
+ }
+ free_commit_list(remoteheads);
+ strbuf_addch(&merge_msg, '\n');
+ commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+ strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
+ finish(result_commit, buf.buf);
+ strbuf_release(&buf);
+ drop_save();
+ return 0;
+}
+
+static int suggest_conflicts(void)
+{
+ FILE *fp;
+ int pos;
+
+ fp = fopen(git_path("MERGE_MSG"), "a");
+ if (!fp)
+ die("Could open %s for writing", git_path("MERGE_MSG"));
+ fprintf(fp, "\nConflicts:\n");
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+
+ if (ce_stage(ce)) {
+ fprintf(fp, "\t%s\n", ce->name);
+ while (pos + 1 < active_nr &&
+ !strcmp(ce->name,
+ active_cache[pos + 1]->name))
+ pos++;
+ }
+ }
+ fclose(fp);
+ rerere();
+ printf("Automatic merge failed; "
+ "fix conflicts and then commit the result.\n");
+ return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+ struct commit *second_token = NULL;
+ if (argc > 1) {
+ unsigned char second_sha1[20];
+
+ if (get_sha1(argv[1], second_sha1))
+ return NULL;
+ second_token = lookup_commit_reference_gently(second_sha1, 0);
+ if (!second_token)
+ die("'%s' is not a commit", argv[1]);
+ if (hashcmp(second_token->object.sha1, head))
+ return NULL;
+ }
+ return second_token;
+}
+
+static int evaluate_result(void)
+{
+ int cnt = 0;
+ struct rev_info rev;
+
+ if (read_cache() < 0)
+ die("failed to read the cache");
+
+ /* Check how many files differ. */
+ init_revisions(&rev, "");
+ setup_revisions(0, NULL, &rev, NULL);
+ rev.diffopt.output_format |=
+ DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = count_diff_files;
+ rev.diffopt.format_callback_data = &cnt;
+ run_diff_files(&rev, 0);
+
+ /*
+ * Check how many unmerged entries are
+ * there.
+ */
+ cnt += count_unmerged_entries();
+
+ return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+ unsigned char result_tree[20];
+ struct strbuf buf;
+ const char *head_arg;
+ int flag, head_invalid = 0, i;
+ int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+ struct commit_list *common = NULL;
+ const char *best_strategy = NULL, *wt_strategy = NULL;
+ struct commit_list **remotes = &remoteheads;
+
+ setup_work_tree();
+ if (unmerged_cache())
+ die("You are in the middle of a conflicted merge.");
+
+ /*
+ * Check if we are _not_ on a detached HEAD, i.e. if there is a
+ * current branch.
+ */
+ branch = resolve_ref("HEAD", head, 0, &flag);
+ if (branch && !prefixcmp(branch, "refs/heads/"))
+ branch += 11;
+ if (is_null_sha1(head))
+ head_invalid = 1;
+
+ git_config(git_merge_config, NULL);
+
+ /* for color.ui */
+ if (diff_use_color_default == -1)
+ diff_use_color_default = git_use_color_default;
+
+ argc = parse_options(argc, argv, builtin_merge_options,
+ builtin_merge_usage, 0);
+
+ if (squash) {
+ if (!allow_fast_forward)
+ die("You cannot combine --squash with --no-ff.");
+ option_commit = 0;
+ }
+
+ if (!argc)
+ usage_with_options(builtin_merge_usage,
+ builtin_merge_options);
+
+ /*
+ * This could be traditional "merge <msg> HEAD <commit>..." and
+ * the way we can tell it is to see if the second token is HEAD,
+ * but some people might have misused the interface and used a
+ * committish that is the same as HEAD there instead.
+ * Traditional format never would have "-m" so it is an
+ * additional safety measure to check for it.
+ */
+ strbuf_init(&buf, 0);
+
+ if (!have_message && is_old_style_invocation(argc, argv)) {
+ strbuf_addstr(&merge_msg, argv[0]);
+ head_arg = argv[1];
+ argv += 2;
+ argc -= 2;
+ } else if (head_invalid) {
+ struct object *remote_head;
+ /*
+ * If the merged head is a valid one there is no reason
+ * to forbid "git merge" into a branch yet to be born.
+ * We do the same for "git pull".
+ */
+ if (argc != 1)
+ die("Can merge only exactly one commit into "
+ "empty head");
+ remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+ if (!remote_head)
+ die("%s - not something we can merge", argv[0]);
+ update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+ DIE_ON_ERR);
+ reset_hard(remote_head->sha1, 0);
+ return 0;
+ } else {
+ struct strbuf msg;
+
+ /* We are invoked directly as the first-class UI. */
+ head_arg = "HEAD";
+
+ /*
+ * All the rest are the commits being merged;
+ * prepare the standard merge summary message to
+ * be appended to the given message. If remote
+ * is invalid we will die later in the common
+ * codepath so we discard the error in this
+ * loop.
+ */
+ strbuf_init(&msg, 0);
+ for (i = 0; i < argc; i++)
+ merge_name(argv[i], &msg);
+ fmt_merge_msg(option_log, &msg, &merge_msg);
+ if (merge_msg.len)
+ strbuf_setlen(&merge_msg, merge_msg.len-1);
+ }
+
+ if (head_invalid || !argc)
+ usage_with_options(builtin_merge_usage,
+ builtin_merge_options);
+
+ strbuf_addstr(&buf, "merge");
+ for (i = 0; i < argc; i++)
+ strbuf_addf(&buf, " %s", argv[i]);
+ setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+ strbuf_reset(&buf);
+
+ for (i = 0; i < argc; i++) {
+ struct object *o;
+
+ o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+ if (!o)
+ die("%s - not something we can merge", argv[i]);
+ remotes = &commit_list_insert(lookup_commit(o->sha1),
+ remotes)->next;
+
+ strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+ setenv(buf.buf, argv[i], 1);
+ strbuf_reset(&buf);
+ }
+
+ if (!use_strategies) {
+ if (!remoteheads->next)
+ add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+ else
+ add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+ }
+
+ for (i = 0; i < use_strategies_nr; i++) {
+ if (use_strategies[i]->attr & NO_FAST_FORWARD)
+ allow_fast_forward = 0;
+ if (use_strategies[i]->attr & NO_TRIVIAL)
+ allow_trivial = 0;
+ }
+
+ if (!remoteheads->next)
+ common = get_merge_bases(lookup_commit(head),
+ remoteheads->item, 1);
+ else {
+ struct commit_list *list = remoteheads;
+ commit_list_insert(lookup_commit(head), &list);
+ common = get_octopus_merge_bases(list);
+ free(list);
+ }
+
+ update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+ DIE_ON_ERR);
+
+ if (!common)
+ ; /* No common ancestors found. We need a real merge. */
+ else if (!remoteheads->next && !common->next &&
+ common->item == remoteheads->item) {
+ /*
+ * If head can reach all the merge then we are up to date.
+ * but first the most common case of merging one remote.
+ */
+ finish_up_to_date("Already up-to-date.");
+ return 0;
+ } else if (allow_fast_forward && !remoteheads->next &&
+ !common->next &&
+ !hashcmp(common->item->object.sha1, head)) {
+ /* Again the most common case of merging one remote. */
+ struct strbuf msg;
+ struct object *o;
+ char hex[41];
+
+ strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+ printf("Updating %s..%s\n",
+ hex,
+ find_unique_abbrev(remoteheads->item->object.sha1,
+ DEFAULT_ABBREV));
+ refresh_cache(REFRESH_QUIET);
+ strbuf_init(&msg, 0);
+ strbuf_addstr(&msg, "Fast forward");
+ if (have_message)
+ strbuf_addstr(&msg,
+ " (no commit created; -m option ignored)");
+ o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+ 0, NULL, OBJ_COMMIT);
+ if (!o)
+ return 1;
+
+ if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+ return 1;
+
+ finish(o->sha1, msg.buf);
+ drop_save();
+ return 0;
+ } else if (!remoteheads->next && common->next)
+ ;
+ /*
+ * We are not doing octopus and not fast forward. Need
+ * a real merge.
+ */
+ else if (!remoteheads->next && !common->next && option_commit) {
+ /*
+ * We are not doing octopus, not fast forward, and have
+ * only one common.
+ */
+ refresh_cache(REFRESH_QUIET);
+ if (allow_trivial) {
+ /* See if it is really trivial. */
+ git_committer_info(IDENT_ERROR_ON_NO_NAME);
+ printf("Trying really trivial in-index merge...\n");
+ if (!read_tree_trivial(common->item->object.sha1,
+ head, remoteheads->item->object.sha1))
+ return merge_trivial();
+ printf("Nope.\n");
+ }
+ } else {
+ /*
+ * An octopus. If we can reach all the remote we are up
+ * to date.
+ */
+ int up_to_date = 1;
+ struct commit_list *j;
+
+ for (j = remoteheads; j; j = j->next) {
+ struct commit_list *common_one;
+
+ /*
+ * Here we *have* to calculate the individual
+ * merge_bases again, otherwise "git merge HEAD^
+ * HEAD^^" would be missed.
+ */
+ common_one = get_merge_bases(lookup_commit(head),
+ j->item, 1);
+ if (hashcmp(common_one->item->object.sha1,
+ j->item->object.sha1)) {
+ up_to_date = 0;
+ break;
+ }
+ }
+ if (up_to_date) {
+ finish_up_to_date("Already up-to-date. Yeeah!");
+ return 0;
+ }
+ }
+
+ /* We are going to make a new commit. */
+ git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+ /*
+ * At this point, we need a real merge. No matter what strategy
+ * we use, it would operate on the index, possibly affecting the
+ * working tree, and when resolved cleanly, have the desired
+ * tree in the index -- this means that the index must be in
+ * sync with the head commit. The strategies are responsible
+ * to ensure this.
+ */
+ if (use_strategies_nr != 1) {
+ /*
+ * Stash away the local changes so that we can try more
+ * than one.
+ */
+ save_state();
+ } else {
+ memcpy(stash, null_sha1, 20);
+ }
+
+ for (i = 0; i < use_strategies_nr; i++) {
+ int ret;
+ if (i) {
+ printf("Rewinding the tree to pristine...\n");
+ restore_state();
+ }
+ if (use_strategies_nr != 1)
+ printf("Trying merge strategy %s...\n",
+ use_strategies[i]->name);
+ /*
+ * Remember which strategy left the state in the working
+ * tree.
+ */
+ wt_strategy = use_strategies[i]->name;
+
+ ret = try_merge_strategy(use_strategies[i]->name,
+ common, head_arg);
+ if (!option_commit && !ret) {
+ merge_was_ok = 1;
+ /*
+ * This is necessary here just to avoid writing
+ * the tree, but later we will *not* exit with
+ * status code 1 because merge_was_ok is set.
+ */
+ ret = 1;
+ }
+
+ if (ret) {
+ /*
+ * The backend exits with 1 when conflicts are
+ * left to be resolved, with 2 when it does not
+ * handle the given merge at all.
+ */
+ if (ret == 1) {
+ int cnt = evaluate_result();
+
+ if (best_cnt <= 0 || cnt <= best_cnt) {
+ best_strategy = use_strategies[i]->name;
+ best_cnt = cnt;
+ }
+ }
+ if (merge_was_ok)
+ break;
+ else
+ continue;
+ }
+
+ /* Automerge succeeded. */
+ write_tree_trivial(result_tree);
+ automerge_was_ok = 1;
+ break;
+ }
+
+ /*
+ * If we have a resulting tree, that means the strategy module
+ * auto resolved the merge cleanly.
+ */
+ if (automerge_was_ok)
+ return finish_automerge(common, result_tree, wt_strategy);
+
+ /*
+ * Pick the result from the best strategy and have the user fix
+ * it up.
+ */
+ if (!best_strategy) {
+ restore_state();
+ if (use_strategies_nr > 1)
+ fprintf(stderr,
+ "No merge strategy handled the merge.\n");
+ else
+ fprintf(stderr, "Merge with strategy %s failed.\n",
+ use_strategies[0]->name);
+ return 2;
+ } else if (best_strategy == wt_strategy)
+ ; /* We already have its result in the working tree. */
+ else {
+ printf("Rewinding the tree to pristine...\n");
+ restore_state();
+ printf("Using the %s to prepare resolving by hand.\n",
+ best_strategy);
+ try_merge_strategy(best_strategy, common, head_arg);
+ }
+
+ if (squash)
+ finish(NULL, NULL);
+ else {
+ int fd;
+ struct commit_list *j;
+
+ for (j = remoteheads; j; j = j->next)
+ strbuf_addf(&buf, "%s\n",
+ sha1_to_hex(j->item->object.sha1));
+ fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die("Could open %s for writing",
+ git_path("MERGE_HEAD"));
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+ die("Could not write to %s", git_path("MERGE_HEAD"));
+ close(fd);
+ strbuf_addch(&merge_msg, '\n');
+ fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die("Could open %s for writing", git_path("MERGE_MSG"));
+ if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+ merge_msg.len)
+ die("Could not write to %s", git_path("MERGE_MSG"));
+ close(fd);
+ }
+
+ if (merge_was_ok) {
+ fprintf(stderr, "Automatic merge went well; "
+ "stopped before committing as requested\n");
+ return 0;
+ } else
+ return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "ls-remote", cmd_ls_remote },
{ "mailinfo", cmd_mailinfo },
{ "mailsplit", cmd_mailsplit },
+ { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
{ "merge-base", cmd_merge_base, RUN_SETUP },
{ "merge-file", cmd_merge_file },
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index f3a4bb2..fcb8285 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
done
'
-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
git reset --hard c1 &&
i=2 &&
refs="" &&
--
1.5.6.1.322.ge904b.dirty
^ permalink raw reply related
* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Junio C Hamano @ 2008-07-08 1:17 UTC (permalink / raw)
To: Josef 'Jeff' Sipek; +Cc: Theodore Ts'o, git
In-Reply-To: <20080708011413.GC1880@josefsipek.net>
"Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:
> A fresh clone (from master.kernel.org over ssh) gets me:
>
> $ git describe
> v1.5.6.2-247-g9237122
> $ git --version
> git version 1.5.6.GIT
> $ git --exec-path
> /home/jeffpc/git-bin/bin
>
> You seem to have a different HEAD for the master branch than I do.
Oh, my private 'master' almost always is ahead of the public one after
work.
But I do not have any new changes around this area lately. Promise ;-).
^ permalink raw reply
* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Junio C Hamano @ 2008-07-08 1:14 UTC (permalink / raw)
To: Stephan Beyer
Cc: Jakub Narebski, git, Sam Vilain, Joshua Roys, Sverre Rabbelier,
Sverre Rabbelier, David Symonds, Lea Wiemann, John Hawley,
Marek Zawirski, Shawn O. Pearce, Miklos Vajna,
Johannes Schindelin, Christian Couder, Daniel Barkalow
In-Reply-To: <20080708010050.GD6726@leksak.fem-net>
Stephan Beyer <s-beyer@gmx.net> writes:
> Imho the prototype patchset needs some more review from others.
Yes, very much. Not just from others and not just from me.
> (Well, I think the
> http://thread.gmane.org/gmane.comp.version-control.git/86985
> thread died in the last days, but I hope some responses will come and
> also that Junio's patch for cherry-picking root commits will be
> included. I try to be patient...) ;-)
Please don't be patient but actively re-review what you sent out.
I _really_ wanted to merge the basic bits and rewrite of "am" at least to
pu tonight, but I had to drop them after noticing that it does not seem to
handle --rebasing at all (it parses to set $rebasing but after that where
does that bit go? bash completion wants to see rebasing or applying
markers in .dotest), which made it a non-starter especially I'll be
cooking the other ORIG_HEAD in 'next' as well.
About the "rewrite rebase to use sequencer" bits, because we've dropped
the older rebase-i change, I do not want your series to depend on it.
^ permalink raw reply
* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Josef 'Jeff' Sipek @ 2008-07-08 1:14 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Theodore Ts'o, git
In-Reply-To: <7vwsjxxkfd.fsf@gitster.siamese.dyndns.org>
On Mon, Jul 07, 2008 at 05:56:54PM -0700, Junio C Hamano wrote:
> "Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:
...
> > jeffpc@freyr:~$ git --version
> > git version 1.5.6.2.247.g923712
> > jeffpc@freyr:~$ git --exec-path
> > /home/jeffpc/git-bin/bin
>
> Hmm, it is yours that look wrong.
Ok.
> With 1.6.0 preview (aka 'master'):
>
> $ git --version
> git version 1.5.6.2.255.gbed62
A fresh clone (from master.kernel.org over ssh) gets me:
$ git describe
v1.5.6.2-247-g9237122
$ git --version
git version 1.5.6.GIT
$ git --exec-path
/home/jeffpc/git-bin/bin
You seem to have a different HEAD for the master branch than I do.
Josef 'Jeff' Sipek.
--
Don't drink and derive. Alcohol and algebra don't mix.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox