aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Documentation/.gitignore1
-rw-r--r--Documentation/Makefile16
-rwxr-xr-xDocumentation/build-docdep.perl28
-rw-r--r--Documentation/git-cvsexportcommit.txt56
-rw-r--r--Documentation/howto/rebase-and-edit.txt2
-rw-r--r--Documentation/howto/revert-branch-rebase.txt3
-rw-r--r--Documentation/howto/using-topic-branches.txt2
-rw-r--r--Makefile2
-rwxr-xr-xgit-cvsexportcommit.perl225
-rwxr-xr-xgit-merge-recursive.py22
-rwxr-xr-x[-rw-r--r--]git-pull.sh0
-rwxr-xr-xgit-tag.sh3
-rw-r--r--http-push.c24
14 files changed, 364 insertions, 24 deletions
diff --git a/.gitignore b/.gitignore
index 3edf6b41a..716c340a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ git-commit
git-commit-tree
git-convert-objects
git-count-objects
+git-cvsexportcommit
git-cvsimport
git-daemon
git-diff
@@ -36,6 +37,7 @@ git-get-tar-commit-id
git-grep
git-hash-object
git-http-fetch
+git-http-push
git-index-pack
git-init-db
git-local-fetch
@@ -107,6 +109,8 @@ git-verify-tag
git-whatchanged
git-write-tree
git-core-*/?*
+test-date
+test-delta
*.tar.gz
*.dsc
*.deb
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index dad52b80d..9fef49087 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -3,3 +3,4 @@
*.1
*.7
howto-index.txt
+doc.dep
diff --git a/Documentation/Makefile b/Documentation/Makefile
index bb9533ff0..f45a06246 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -53,21 +53,19 @@ install: man
#
# Determine "include::" file references in asciidoc files.
#
-TEXTFILES = $(wildcard *.txt)
-DEPFILES = $(TEXTFILES:%.txt=%.dep)
-
-%.dep : %.txt
- @rm -f $@
- @$(foreach dep, $(shell grep include:: $< | sed -e 's/include::/ /' -e 's/\[\]//'), \
- echo $(<:%.txt=%.html) $(<:%.txt=%.1) : $(dep) >> $@; )
+TEXTFILES = $(wildcard git-*.txt)
+doc.dep : $(TEXTFILES) build-docdep.perl
+ rm -f $@+ $@
+ perl ./build-docdep.perl >$@+
+ mv $@+ $@
--include $(DEPFILES)
+-include doc.dep
git.7: ../README
clean:
- rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html *.dep
+ rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep
%.html : %.txt
asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
diff --git a/Documentation/build-docdep.perl b/Documentation/build-docdep.perl
new file mode 100755
index 000000000..dedef765a
--- /dev/null
+++ b/Documentation/build-docdep.perl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+my %include = ();
+
+for my $text (<git-*.txt>) {
+ open I, '<', $text || die "cannot read: $text";
+ (my $base = $text) =~ s/\.txt$//;
+ while (<I>) {
+ if (/^include::/) {
+ chomp;
+ s/^include::\s*//;
+ s/\[\]//;
+ $include{$base}{$_} = 1;
+ }
+ }
+ close I;
+}
+
+# Do we care about chained includes???
+
+while (my ($base, $included) = each %include) {
+ my ($suffix) = '1';
+ if ($base eq 'git') {
+ $suffix = '7'; # yuck...
+ }
+ print "$base.html $base.$suffix : ", join(" ", keys %$included), "\n";
+}
+
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
new file mode 100644
index 000000000..c3da73d1c
--- /dev/null
+++ b/Documentation/git-cvsexportcommit.txt
@@ -0,0 +1,56 @@
+git-cvsexportcommit(1)
+======================
+
+NAME
+----
+git-cvsexportcommit - Export a commit to a CVS checkout
+
+
+SYNOPSIS
+--------
+git-cvsapplycommmit.perl
+ [ -h ] [ -v ] [ -c ] [ -p ] [PARENTCOMMIT] COMMITID
+
+
+DESCRIPTION
+-----------
+Exports a commit from GIT to a CVS checkout, making it easier
+to merge patches from a git repository into a CVS repository.
+
+Execute it from the root of the CVS working copy. GIT_DIR must be defined.
+
+It does its best to do the safe thing, it will check that the files are
+unchanged and up to date in the CVS checkout, and it will not autocommit
+by default.
+
+Supports file additions, removals, and commits that affect binary files.
+
+If the commit is a merge commit, you must tell git-cvsapplycommit what parent
+should the changeset be done against.
+
+OPTIONS
+-------
+
+-c::
+ Commit automatically if the patch applied cleanly. It will not
+ commit if any hunks fail to apply or there were other problems.
+
+-p::
+ Be pedantic (paranoid) when applying patches. Invokes patch with
+ --fuzz=0
+
+-v::
+ Verbose.
+
+Author
+------
+Written by Martin Langhoff <martin@catalyst.net.nz>
+
+Documentation
+--------------
+Documentation by Martin Langhoff <martin@catalyst.net.nz>
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Documentation/howto/rebase-and-edit.txt b/Documentation/howto/rebase-and-edit.txt
index 6cc1c7921..646c55cc6 100644
--- a/Documentation/howto/rebase-and-edit.txt
+++ b/Documentation/howto/rebase-and-edit.txt
@@ -63,7 +63,7 @@ And then, you can just remove the broken branch if you decide you really
don't want it:
# remove 'broken' branch
- rm .git/refs/heads/broken
+ git branch -d broken
# Prune old objects if you're really really sure
git prune
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index e4cce5bf7..5a7e0cfe0 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -153,7 +153,8 @@ Everything is in the good order. I do not need the temporary branch
nor tag anymore, so remove them:
------------------------------------------------
-$ rm -f .git/refs/tags/pu-anchor .git/refs/heads/revert-c99
+$ rm -f .git/refs/tags/pu-anchor
+$ git branch -d revert-c99
------------------------------------------------
It was an emergency fix, so we might as well merge it into the
diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt
index d30fa8504..c6c635a51 100644
--- a/Documentation/howto/using-topic-branches.txt
+++ b/Documentation/howto/using-topic-branches.txt
@@ -166,7 +166,7 @@ output from:
is empty. At this point the branch can be deleted:
- $ rm .git/refs/heads/branchname
+ $ git branch -d branchname
Some changes are so trivial that it is not necessary to create a separate
branch and then merge into each of the test and release branches. For
diff --git a/Makefile b/Makefile
index 56e0fb1ba..ba1c6a736 100644
--- a/Makefile
+++ b/Makefile
@@ -93,7 +93,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
git-rename.perl git-shortlog.perl git-fmt-merge-msg.perl \
- git-svnimport.perl git-mv.perl
+ git-svnimport.perl git-mv.perl git-cvsexportcommit.perl
SCRIPT_PYTHON = \
git-merge-recursive.py
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
new file mode 100755
index 000000000..7074b0c21
--- /dev/null
+++ b/git-cvsexportcommit.perl
@@ -0,0 +1,225 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Std;
+use File::Temp qw(tempdir);
+use Data::Dumper;
+
+unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
+ die "GIT_DIR is not defined or is unreadable";
+}
+
+our ($opt_h, $opt_p, $opt_v, $opt_c );
+
+getopt('hpvc');
+
+$opt_h && usage();
+
+die "Need at least one commit identifier!" unless @ARGV;
+
+# setup a tempdir
+our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX',
+ TMPDIR => 1,
+ CLEANUP => 1);
+
+print Dumper(@ARGV);
+# resolve target commit
+my $commit;
+$commit = pop @ARGV;
+$commit = `git-rev-parse --verify "$commit"^0`;
+chomp $commit;
+if ($?) {
+ die "The commit reference $commit did not resolve!";
+}
+
+# resolve what parent we want
+my $parent;
+if (@ARGV) {
+ $parent = pop @ARGV;
+ $parent = `git-rev-parse --verify "$parent"^0"`;
+ chomp $parent;
+ if ($?) {
+ die "The parent reference did not resolve!";
+ }
+}
+
+# find parents from the commit itself
+my @commit = `git-cat-file commit $commit`;
+my @parents;
+foreach my $p (@commit) {
+ if ($p =~ m/^$/) { # end of commit headers, we're done
+ last;
+ }
+ if ($p =~ m/^parent (\w{40})$/) { # found a parent
+ push @parents, $1;
+ }
+}
+
+if ($parent) {
+ # double check that it's a valid parent
+ foreach my $p (@parents) {
+ my $found;
+ if ($p eq $parent) {
+ $found = 1;
+ last;
+ }; # found it
+ die "Did not find $parent in the parents for this commit!";
+s }
+} else { # we don't have a parent from the cmdline...
+ if (@parents == 1) { # it's safe to get it from the commit
+ $parent = $parents[0];
+ } else { # or perhaps not!
+ die "This commit has more than one parent -- please name the parent you want to use explicitly";
+ }
+}
+
+$opt_v && print "Applying to CVS commit $commit from parent $parent\n";
+
+# grab the commit message
+`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`;
+$? && die "Error extraction the commit message";
+
+my (@afiles, @dfiles, @mfiles);
+my @files = `git-diff-tree -r $parent $commit`;
+print @files;
+$? && die "Error in git-diff-tree";
+foreach my $f (@files) {
+ chomp $f;
+ my @fields = split(m/\s+/, $f);
+ if ($fields[4] eq 'A') {
+ push @afiles, $fields[5];
+ }
+ if ($fields[4] eq 'M') {
+ push @mfiles, $fields[5];
+ }
+ if ($fields[4] eq 'R') {
+ push @dfiles, $fields[5];
+ }
+}
+$opt_v && print "The commit affects:\n ";
+$opt_v && print join ("\n ", @afiles,@mfiles,@dfiles) . "\n\n";
+undef @files; # don't need it anymore
+
+# check that the files are clean and up to date according to cvs
+my $dirty;
+foreach my $f (@afiles, @mfiles, @dfiles) {
+ # TODO:we need to handle removed in cvs and/or new (from git)
+ my $status = `cvs -q status "$f" | grep '^File: '`;
+
+ unless ($status =~ m/Status: Up-to-date$/) {
+ $dirty = 1;
+ warn "File $f not up to date in your CVS checkout!\n";
+ }
+}
+if ($dirty) {
+ die "Exiting: your CVS tree is not clean for this merge.";
+}
+
+###
+### NOTE: if you are planning to die() past this point
+### you MUST call cleanupcvs(@files) before die()
+###
+
+
+print "'Patching' binary files\n";
+
+my @bfiles = `git-diff-tree -p $parent $commit | grep '^Binary'`;
+@bfiles = map { chomp } @bfiles;
+foreach my $f (@bfiles) {
+ # check that the file in cvs matches the "old" file
+ # extract the file to $tmpdir and comparre with cmp
+ my $tree = `git-rev-parse $parent^{tree} `;
+ chomp $tree;
+ my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
+ chomp $blob;
+ `git-cat-file blob $blob > $tmpdir/blob`;
+ `cmp -q $f $tmpdir/blob`;
+ if ($?) {
+ warn "Binary file $f in CVS does not match parent.\n";
+ $dirty = 1;
+ next;
+ }
+
+ # replace with the new file
+ `git-cat-file blob $blob > $f`;
+
+ # TODO: something smart with file modes
+
+}
+if ($dirty) {
+ cleanupcvs(@files);
+ die "Exiting: Binary files in CVS do not match parent";
+}
+
+## apply non-binary changes
+my $fuzz = $opt_p ? 0 : 2;
+
+print "Patching non-binary files\n";
+print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
+
+my $dirtypatch = 0;
+if (($? >> 8) == 2) {
+ cleanupcvs(@files);
+ die "Exiting: Patch reported serious trouble -- you will have to apply this patch manually";
+} elsif (($? >> 8) == 1) { # some hunks failed to apply
+ $dirtypatch = 1;
+}
+
+foreach my $f (@afiles) {
+ `cvs add $f`;
+ if ($?) {
+ $dirty = 1;
+ warn "Failed to cvs add $f -- you may need to do it manually";
+ }
+}
+
+foreach my $f (@dfiles) {
+ `cvs rm -f $f`;
+ if ($?) {
+ $dirty = 1;
+ warn "Failed to cvs rm -f $f -- you may need to do it manually";
+ }
+}
+
+print "Commit to CVS\n";
+my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
+my $cmd = "cvs commit -F .msg $commitfiles";
+
+if ($dirtypatch) {
+ print "NOTE: One or more hunks failed to apply cleanly.\n";
+ print "Resolve the conflicts and then commit using:n";
+ print "\n $cmd\n\n";
+ exit;
+}
+
+
+if ($opt_c) {
+ print "Autocommit\n $cmd\n";
+ print `cvs commit -F .msg $commitfiles 2>&1`;
+ if ($?) {
+ cleanupcvs(@files);
+ die "Exiting: The commit did not succeed";
+ }
+ print "Committed successfully to CVS\n";
+} else {
+ print "Ready for you to commit, just run:\n\n $cmd\n";
+}
+sub usage {
+ print STDERR <<END;
+Usage: GIT_DIR=/path/to/.gi ${\basename $0} # fetch/update GIT from CVS
+ [-h] [-p] [ parent ] commit
+END
+ exit(1);
+}
+
+# ensure cvs is clean before we die
+sub cleanupcvs {
+ my @files = @_;
+ foreach my $f (@files) {
+ `cvs -q update -C "$f"`;
+ if ($?) {
+ warn "Warning! Failed to cleanup state of $f\n";
+ }
+ }
+}
+
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index 626d85493..9983cd9de 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -162,10 +162,13 @@ def mergeTrees(head, merge, common, branch1Name, branch2Name):
# Low level file merging, update and removal
# ------------------------------------------
+MERGE_NONE = 0
+MERGE_TRIVIAL = 1
+MERGE_3WAY = 2
def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
branch1Name, branch2Name):
- merge = False
+ merge = MERGE_NONE
clean = True
if stat.S_IFMT(aMode) != stat.S_IFMT(bMode):
@@ -178,7 +181,7 @@ def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
sha = bSha
else:
if aSha != oSha and bSha != oSha:
- merge = True
+ merge = MERGE_TRIVIAL
if aMode == oMode:
mode = bMode
@@ -207,7 +210,8 @@ def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
os.unlink(orig)
os.unlink(src1)
os.unlink(src2)
-
+
+ merge = MERGE_3WAY
clean = (code == 0)
else:
assert(stat.S_ISLNK(aMode) and stat.S_ISLNK(bMode))
@@ -577,14 +581,16 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
updateFile(False, ren1.dstSha, ren1.dstMode, dstName1)
updateFile(False, ren2.dstSha, ren2.dstMode, dstName2)
else:
- print 'Renaming', fmtRename(path, ren1.dstName)
[resSha, resMode, clean, merge] = \
mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
ren1.dstName, ren1.dstSha, ren1.dstMode,
ren2.dstName, ren2.dstSha, ren2.dstMode,
branchName1, branchName2)
- if merge:
+ if merge or not clean:
+ print 'Renaming', fmtRename(path, ren1.dstName)
+
+ if merge == MERGE_3WAY:
print 'Auto-merging', ren1.dstName
if not clean:
@@ -653,14 +659,16 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
tryMerge = True
if tryMerge:
- print 'Renaming', fmtRename(ren1.srcName, ren1.dstName)
[resSha, resMode, clean, merge] = \
mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
ren1.dstName, ren1.dstSha, ren1.dstMode,
ren1.srcName, srcShaOtherBranch, srcModeOtherBranch,
branchName1, branchName2)
- if merge:
+ if merge or not clean:
+ print 'Renaming', fmtRename(ren1.srcName, ren1.dstName)
+
+ if merge == MERGE_3WAY:
print 'Auto-merging', ren1.dstName
if not clean:
diff --git a/git-pull.sh b/git-pull.sh
index 2358af62d..2358af62d 100644..100755
--- a/git-pull.sh
+++ b/git-pull.sh
diff --git a/git-tag.sh b/git-tag.sh
index bd9275367..6130904a9 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -92,5 +92,6 @@ if [ "$annotate" ]; then
object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
fi
-mkdir -p "$GIT_DIR/refs/tags"
+leading=`expr "refs/tags/$name" : '\(.*\)/'` &&
+mkdir -p "$GIT_DIR/$leading" &&
echo $object > "$GIT_DIR/refs/tags/$name"
diff --git a/http-push.c b/http-push.c
index 89fda42ef..0b90fb9ae 100644
--- a/http-push.c
+++ b/http-push.c
@@ -28,6 +28,15 @@ static const char http_push_usage[] =
#define NO_CURL_EASY_DUPHANDLE
#endif
+#ifndef XML_STATUS_OK
+enum XML_Status {
+ XML_STATUS_OK = 1,
+ XML_STATUS_ERROR = 0
+};
+#define XML_STATUS_OK 1
+#define XML_STATUS_ERROR 0
+#endif
+
#define RANGE_HEADER_SIZE 30
/* DAV method names and request body templates */
@@ -47,6 +56,7 @@ static int active_requests = 0;
static int data_received;
static int pushing = 0;
static int aborted = 0;
+static char remote_dir_exists[256];
#ifdef USE_CURL_MULTI
static int max_requests = -1;
@@ -650,6 +660,7 @@ static void finish_request(struct transfer_request *request)
if (request->http_code == 404) {
request->state = NEED_PUSH;
} else if (request->curl_result == CURLE_OK) {
+ remote_dir_exists[request->sha1[0]] = 1;
request->state = COMPLETE;
} else {
fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
@@ -661,6 +672,7 @@ static void finish_request(struct transfer_request *request)
} else if (request->state == RUN_MKCOL) {
if (request->curl_result == CURLE_OK ||
request->http_code == 405) {
+ remote_dir_exists[request->sha1[0]] = 1;
start_put(request);
} else {
fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
@@ -728,11 +740,12 @@ void process_curl_messages(void)
slot->curl != curl_message->easy_handle)
slot = slot->next;
if (slot != NULL) {
+ int curl_result = curl_message->data.result;
curl_multi_remove_handle(curlm, slot->curl);
active_requests--;
slot->done = 1;
slot->in_use = 0;
- slot->curl_result = curl_message->data.result;
+ slot->curl_result = curl_result;
curl_easy_getinfo(slot->curl,
CURLINFO_HTTP_CODE,
&slot->http_code);
@@ -767,7 +780,10 @@ void process_request_queue(void)
start_check(request);
curl_multi_perform(curlm, &num_transfers);
} else if (pushing && request->state == NEED_PUSH) {
- start_mkcol(request);
+ if (remote_dir_exists[request->sha1[0]])
+ start_put(request);
+ else
+ start_mkcol(request);
curl_multi_perform(curlm, &num_transfers);
}
request = request->next;
@@ -1230,7 +1246,7 @@ struct active_lock *lock_remote(char *file, long timeout)
in_buffer.posn = 0;
in_buffer.buffer = in_data;
- new_lock = xmalloc(sizeof(*new_lock));
+ new_lock = xcalloc(1, sizeof(*new_lock));
new_lock->owner = NULL;
new_lock->token = NULL;
new_lock->timeout = -1;
@@ -1599,6 +1615,8 @@ int main(int argc, char **argv)
break;
}
+ memset(remote_dir_exists, 0, 256);
+
curl_global_init(CURL_GLOBAL_ALL);
#ifdef USE_CURL_MULTI