16 # Import an external git repository into the OpenAFS tree, taking the path
17 # to a local clone of that repository, a file containing a list of mappings
18 # between that repository and the location in the OpenAFS one, and optionally
25 my $result = GetOptions("help|?" => \$help,
26 "nofixwhitespace" => \$nowhitespace,
28 "externaldir=s" => \$externalDir);
30 pod2usage(1) if $help;
31 pod2usage(-existatus => 0, -verbose =>2) if $man;
34 my $clonePath = shift;
35 my $commitish = shift;
37 pod2usage(2) if !defined($module) || !defined($clonePath);
43 # Use the PROGRAM_NAME to work out where we should be importing to.
45 $externalDir = dirname(Cwd::abs_path($PROGRAM_NAME));
48 # Read in our mapping file
50 my $fh = IO::File->new("$externalDir/$module-files")
51 or die "Couldn't open mapping file : $!\n";
54 if (/^(\S+)\s+(\S+)$/) {
57 die "Unrecognised line in mapping file : $_\n";
62 # Read in our last-sha1 file
65 $fh = IO::File->new("$externalDir/$module-last");
73 $fh = IO::File->new("$externalDir/$module-author");
75 $author = $fh->getline;
80 # Create the external directory, if it doesn't exist.
81 mkdir "$externalDir/$module" if (! -d "$externalDir/$module");
83 # Make ourselves a temporary directory
84 my $tempdir = File::Temp::tempdir(CLEANUP => 1);
86 # Write a list of all of the files that we're going to want out of the other
87 # repository in a format we can use with tar.
88 $fh = IO::File->new($tempdir."/filelist", "w")
89 or die "Can't open temporary file list for writing\n";
90 foreach (sort keys(%mapping)) {
91 $fh->print("source/".$_."\n");
95 # Change directory to the root of the source repository
97 or die "Unable to change directory to $clonePath : $!\n";
99 # Figure out some better names for the commit object we're using
100 my $commitSha1 = `git rev-parse $commitish`;
101 my $commitDesc = `git describe $commitish`;
105 # If we know what our last import was, then get a list of all of the changes
109 my $filelist = join(' ', sort keys(%mapping));
110 $changes = `git shortlog $last..$commitish $filelist`;
113 # Populate our temporary directory with the originals of everything that was
114 # listed in the mapping file
115 system("git archive --format=tar --prefix=source/ $commitish".
116 " | tar -x -C $tempdir -T $tempdir/filelist") == 0
117 or die "git archive and tar failed : $!\n";
119 # change our CWD to the module directory - git ls-files seems to require this
120 chdir "$externalDir/$module"
121 or die "Unable to change directory to $externalDir/$module : $!\n";
123 # Now we're about to start fiddling with local state. Make a note of where we
126 # Use git stash to preserve whatever state there may be in the current
127 # working tree. Sadly git stash returns a 0 exit status if there are no
128 # local changes, so we need to check for local changes first.
131 if (system("git diff-index --quiet --cached HEAD --ignore-submodules") != 0 ||
132 system("git diff-files --quiet --ignore-submodules") != 0) {
133 if (system("git stash") != 0) {
134 die "git stash failed with : $!\n";
144 # Use git-ls-files to get the list of currently committed files for the module
145 my $lspipe = IO::Pipe->new();
146 $lspipe->reader(qw(git ls-files));
154 foreach my $source (sort keys(%mapping)) {
155 if (-f "$tempdir/source/$source") {
156 File::Path::make_path(File::Basename::dirname($mapping{$source}));
157 if (!-f "$externalDir/$module/".$mapping{$source}) {
158 push @addedFiles, $mapping{$source};
160 system("cp $tempdir/source/$source ".
161 " $externalDir/$module/".$mapping{$source}) == 0
162 or die "Copy failed with $!\n";
163 system("git add $externalDir/$module/".$mapping{$source}) == 0
164 or die "git add failed with $!\n";
165 delete $filesInTree{$mapping{$source}}
167 die "Couldn't find file $source in original tree\n";
171 # Use git rm to delete everything that's committed that we don't have a
173 foreach my $missing (keys(%filesInTree)) {
174 system("git rm $missing") == 0
175 or die "Couldn't git rm $missing : $!\n";
176 push @deletedFiles, $missing;
179 if (system("git status") == 0) {
180 my $fh=IO::File->new("$externalDir/$module-last", "w");
181 $fh->print($commitSha1."\n");
183 system("git add $externalDir/$module-last") == 0
184 or die "Git add of last file failed with $!\n";
186 $fh=IO::File->new("$tempdir/commit-msg", "w")
187 or die "Unable to write commit message\n";
188 $fh->print("Import of code from $module\n");
190 $fh->print("This commit updates the code imported from $module to\n");
191 $fh->print("$commitSha1 ($commitDesc)\n");
194 $fh->print("Upstream changes are:\n\n");
195 $fh->print($changes);
199 $fh->print("New files are:\n");
200 $fh->print(join("\n", map { "\t".$_ } sort @addedFiles));
205 $fh->print("Deleted files are:\n");
206 $fh->print(join("\n", map { "\t".$_ } sort @deletedFiles));
210 $author="--author '$author'" if ($author);
211 system("git commit -F $tempdir/commit-msg $author") == 0
212 or die "Commit failed : $!\n";
214 print STDERR "WARNING: not fixing whitespace errors.\n";
216 system("git rebase --whitespace=fix HEAD^") == 0
217 or print STDERR "WARNING: Fixing whitespace errors failed.\n";
225 print STDERR "Import failed with $@\n";
226 print STDERR "Attempting to reset back to where we were ...\n";
227 system("git reset --hard HEAD") == 0
228 or die "Unable to reset, sorry. You'll need to pick up the pieces\n";
233 system("git stash pop") == 0
234 or die "git stash pop failed with : $!\n";
243 import-external-git - Import bits of an external git repo to OpenAFS
247 import-external-git [options] <module> <repository> [<commitish>]
250 --help brief help message
251 --man full documentation
252 --externalDir exact path to import into
253 --nofixwhitespace don't apply whitespace fixes
257 import-external-git imports selected files from an external git repository
258 into the OpenAFS src/external tree. For a given <module> it assumes that
259 src/external/<module>-files already exists, and contains a space separated
260 list of source and destination file names. <repository> should point to a
261 local clone of the external project's git repository, and <commitish> points
262 to an object within that tree. If <commitish> isn't specified, the current
263 branch HEAD of that repository is used.