diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Documentation/.gitignore | 1 | ||||
-rw-r--r-- | Documentation/Makefile | 16 | ||||
-rwxr-xr-x | Documentation/build-docdep.perl | 28 | ||||
-rw-r--r-- | Documentation/git-cvsexportcommit.txt | 56 | ||||
-rw-r--r-- | Documentation/howto/rebase-and-edit.txt | 2 | ||||
-rw-r--r-- | Documentation/howto/revert-branch-rebase.txt | 3 | ||||
-rw-r--r-- | Documentation/howto/using-topic-branches.txt | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | git-cvsexportcommit.perl | 225 | ||||
-rwxr-xr-x | git-merge-recursive.py | 22 | ||||
-rwxr-xr-x[-rw-r--r--] | git-pull.sh | 0 | ||||
-rwxr-xr-x | git-tag.sh | 3 | ||||
-rw-r--r-- | http-push.c | 24 |
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 @@ -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 |