2 * Copyright © 2006 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at
7 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 #include <sys/types.h>
26 #define MAXPATHLEN 10240
31 rev_execution_mode rev_mode
= ExecuteGit
;
37 const char *log_command
;
40 dump_number_file (FILE *f
, char *name
, cvs_number
*number
)
43 fprintf (f
, "%s ", name
);
45 for (i
= 0; i
< number
->c
; i
++) {
46 fprintf (f
, "%d", number
->n
[i
]);
47 if (i
< number
->c
- 1) fprintf (f
, ".");
53 dump_number (char *name
, cvs_number
*number
)
55 dump_number_file (stdout
, name
, number
);
59 dump_symbols (char *name
, cvs_symbol
*symbols
)
61 printf ("%s\n", name
);
64 dump_number (symbols
->name
, &symbols
->number
);
66 symbols
= symbols
->next
;
71 dump_branches (char *name
, cvs_branch
*branches
)
75 dump_number (" ", &branches
->number
);
76 branches
= branches
->next
;
82 dump_versions (char *name
, cvs_version
*versions
)
84 printf ("%s\n", name
);
86 dump_number ("\tnumber:", &versions
->number
); printf ("\n");
87 printf ("\t\tdate: %s", ctime (&versions
->date
));
88 printf ("\t\tauthor: %s\n", versions
->author
);
89 dump_branches("\t\tbranches:", versions
->branches
);
90 dump_number ("\t\tparent: ", &versions
->parent
); printf ("\n");
91 if (versions
->commitid
)
92 printf ("\t\tcommitid: %s\n", versions
->commitid
);
94 versions
= versions
->next
;
99 dump_patches (char *name
, cvs_patch
*patches
)
101 printf ("%s\n", name
);
103 dump_number ("\tnumber: ", &patches
->number
); printf ("\n");
104 printf ("\t\tlog: %d bytes\n", (int)strlen (patches
->log
));
105 printf ("\t\ttext: %d bytes\n", (int)strlen (patches
->text
));
106 patches
= patches
->next
;
111 dump_file (cvs_file
*file
)
113 dump_number ("head", &file
->head
); printf ("\n");
114 dump_number ("branch", &file
->branch
); printf ("\n");
115 dump_symbols ("symbols", file
->symbols
);
116 dump_versions ("versions", file
->versions
);
117 dump_patches ("patches", file
->patches
);
121 dump_log (FILE *f
, char *log
)
124 for (j
= 0; j
< 48; j
++) {
127 if (log
[j
] == '\n') {
135 if (log
[j
] == '(' || log
[j
] == ')' ||
136 log
[j
] == '[' || log
[j
] == ']' ||
137 log
[j
] == '{' || log
[j
] == '}')
142 if (log
[j
] == '.' && isspace (log
[j
+1]))
148 dump_commit_graph (rev_commit
*c
, rev_ref
*branch
)
155 dump_ref_name (stdout
, branch
);
157 // printf ("*** TAIL");
159 printf ("%s\\n", ctime_nonl (&c
->date
));
160 dump_log (stdout
, c
->log
);
163 rev_diff
*diff
= rev_commit_diff (c
->parent
, c
);
166 for (fl
= diff
->add
; fl
; fl
= fl
->next
) {
167 if (!rev_file_list_has_filename (diff
->del
, fl
->file
->name
)) {
169 dump_number (fl
->file
->name
, &fl
->file
->number
);
173 for (fl
= diff
->add
; fl
; fl
= fl
->next
) {
174 if (rev_file_list_has_filename (diff
->del
, fl
->file
->name
)) {
176 dump_number (fl
->file
->name
, &fl
->file
->number
);
180 for (fl
= diff
->del
; fl
; fl
= fl
->next
) {
181 if (!rev_file_list_has_filename (diff
->add
, fl
->file
->name
)) {
183 dump_number (fl
->file
->name
, &fl
->file
->number
);
187 rev_diff_free (diff
);
190 dump_number (c
->file
->name
, &c
->file
->number
);
193 for (i
= 0; i
< c
->ndirs
; i
++) {
194 rev_dir
*dir
= c
->dirs
[i
];
195 for (j
= 0; j
< dir
->nfiles
; j
++) {
197 dump_number (f
->name
, &f
->number
);
208 dump_ref_name (FILE *f
, rev_ref
*ref
)
211 dump_ref_name (f
, ref
->parent
);
214 fprintf (f
, "%s", ref
->name
);
217 static void dump_tag_name(FILE *f
, Tag
*tag
)
220 dump_ref_name (f
, tag
->parent
);
223 fprintf (f
, "%s", tag
->name
);
228 dump_find_branch (rev_list
*rl
, rev_commit
*commit
)
233 for (h
= rl
->heads
; h
; h
= h
->next
)
237 for (c
= h
->commit
; c
; c
= c
->parent
)
249 dump_refs (rev_list
*rl
, rev_ref
*refs
, char *title
, char *shape
)
254 for (r
= refs
; r
; r
= r
->next
) {
259 printf ("%s\\n", title
);
263 for (o
= r
; o
; o
= o
->next
)
264 if (!o
->shown
&& o
->commit
== r
->commit
)
269 dump_ref_name (stdout
, o
);
270 printf (" (%d)", o
->degree
);
273 printf ("\" [fontsize=6,fixedsize=false,shape=%s];\n", shape
);
276 for (r
= refs
; r
; r
= r
->next
)
278 for (r
= refs
; r
; r
= r
->next
) {
283 printf ("%s\\n", title
);
287 for (o
= r
; o
; o
= o
->next
)
288 if (!o
->shown
&& o
->commit
== r
->commit
)
293 dump_ref_name (stdout
, o
);
294 printf (" (%d)", o
->degree
);
300 dump_commit_graph (r
->commit
, dump_find_branch (rl
,
304 printf (" [weight=%d];\n", !r
->tail
? 100 : 3);
307 for (r
= refs
; r
; r
= r
->next
)
311 static void dump_tags(rev_list
*rl
, char *title
, char *shape
)
321 for (r
= all_tags
, count
= 0; r
; r
= r
->next
, count
++)
324 v
= calloc(count
, sizeof(*v
));
326 for (r
= all_tags
, i
= 0; r
; r
= r
->next
)
329 for (i
= 0; i
< count
; i
++) {
335 printf ("%s\\n", title
);
336 dump_tag_name(stdout
, r
);
337 for (n
= i
+ 1; n
< count
; n
++) {
338 if (v
[n
].t
->commit
== r
->commit
) {
341 dump_tag_name(stdout
, v
[n
].t
);
344 printf ("\" [fontsize=6,fixedsize=false,shape=%s];\n", shape
);
346 for (i
= 0; i
< count
; i
++) {
352 printf ("%s\\n", title
);
353 dump_tag_name(stdout
, r
);
354 for (n
= i
+ 1; n
< count
; n
++) {
355 if (v
[n
].alias
&& v
[n
].t
->commit
== r
->commit
) {
357 dump_tag_name(stdout
, v
[n
].t
);
362 dump_commit_graph (r
->commit
, dump_find_branch (rl
, r
->commit
));
365 printf (" [weight=3];\n");
371 dump_get_rev_parent (rev_commit
*c
)
376 while (elide
&& c
&& c
->seen
== seen
&& !c
->tail
&& !c
->tagged
)
382 dump_rev_graph_nodes (rev_list
*rl
, char *title
)
388 printf ("nodesep=0.1;\n");
389 printf ("ranksep=0.1;\n");
390 printf ("edge [dir=none];\n");
391 printf ("node [shape=box,fontsize=6];\n");
392 dump_refs (rl
, rl
->heads
, title
, "ellipse");
393 dump_tags (rl
, title
, "diamond");
394 for (h
= rl
->heads
; h
; h
= h
->next
) {
397 for (c
= h
->commit
; c
; c
= p
) {
398 p
= dump_get_rev_parent (c
);
403 dump_commit_graph (c
, h
);
405 dump_commit_graph (p
, tail
? h
->parent
: h
);
407 printf (" [weight=10];");
416 dump_rev_graph_begin ()
418 printf ("digraph G {\n");
422 dump_rev_graph_end ()
428 dump_rev_graph (rev_list
*rl
, char *title
)
430 dump_rev_graph_begin ();
431 dump_rev_graph_nodes (rl
, title
);
432 dump_rev_graph_end ();
436 dump_rev_commit (rev_commit
*c
)
441 for (i
= 0; i
< c
->ndirs
; i
++) {
442 rev_dir
*dir
= c
->dirs
[i
];
444 for (j
= 0; j
< dir
->nfiles
; j
++) {
446 dump_number (f
->name
, &f
->number
);
454 dump_rev_head (rev_ref
*h
)
457 for (c
= h
->commit
; c
; c
= c
->parent
) {
465 dump_rev_list (rev_list
*rl
)
469 for (h
= rl
->heads
; h
; h
= h
->next
) {
482 rev_list_file (char *name
, int *nversions
)
487 yyin
= fopen (name
, "r");
494 this_file
= calloc (1, sizeof (cvs_file
));
495 this_file
->name
= name
;
497 assert (fstat (fileno (yyin
), &buf
) == 0);
498 this_file
->mode
= buf
.st_mode
;
502 rl
= rev_list_cvs (this_file
);
504 *nversions
= this_file
->nversions
;
505 cvs_file_free (this_file
);
509 typedef struct _rev_split
{
510 struct _rev_split
*next
;
511 rev_commit
*childa
, *childb
;
513 rev_commit
*topa
, *topb
;
517 ctime_nonl (time_t *date
)
519 char *d
= ctime (date
);
521 d
[strlen(d
)-1] = '\0';
526 dump_splits (rev_list
*rl
)
529 rev_split
*splits
= NULL
, *s
;
531 rev_commit
*c
, *a
, *b
;
536 /* Find tails and mark splits */
537 for (head
= rl
->heads
; head
; head
= head
->next
) {
540 for (c
= head
->commit
; c
; c
= c
->parent
)
542 for (s
= splits
; s
; s
= s
->next
)
543 if (s
->parent
== c
->parent
)
546 s
= calloc (1, sizeof (rev_split
));
547 s
->parent
= c
->parent
;
549 s
->topa
= head
->commit
;
555 /* Find join points */
556 for (s
= splits
; s
; s
= s
->next
) {
557 for (head
= rl
->heads
; head
; head
= head
->next
) {
560 for (c
= head
->commit
; c
; c
= c
->parent
) {
561 if (c
->parent
== s
->parent
&& c
!= s
->childa
) {
563 s
->topb
= head
->commit
;
568 for (s
= splits
; s
; s
= s
->next
) {
569 if (s
->parent
&& s
->childa
&& s
->childb
) {
570 for (head
= rl
->heads
; head
; head
= head
->next
) {
571 if (head
->commit
== s
->topa
)
572 fprintf (stderr
, "%s ", head
->name
);
574 fprintf (stderr
, "->");
575 for (head
= rl
->heads
; head
; head
= head
->next
) {
576 if (head
->commit
== s
->topb
)
577 fprintf (stderr
, "%s ", head
->name
);
579 fprintf (stderr
, "\n");
583 while (ai
< a
->nfiles
&& bi
< b
->nfiles
) {
587 if (rev_file_later (af
, bf
)) {
588 fprintf (stderr
, "a : %s ", ctime_nonl (&af
->date
));
589 dump_number_file (stderr
, af
->name
, &af
->number
);
592 fprintf (stderr
, " b: %s ", ctime_nonl (&bf
->date
));
593 dump_number_file (stderr
, bf
->name
, &bf
->number
);
596 fprintf (stderr
, "\n");
598 // fprintf (stderr, "ab: %s ", ctime_nonl (&af->date));
599 // dump_number_file (stderr, af->name, &af->number);
600 // fprintf (stderr, "\n");
606 if (ai
>= a
->nfiles
) {
611 while (ai
< a
->nfiles
) {
613 fprintf (stderr
, "%s: ", which
);
614 dump_number_file (stderr
, af
->name
, &af
->number
);
615 fprintf (stderr
, "\n");
624 dump_rev_tree (rev_list
*rl
)
631 printf ("rev_list {\n");
633 for (h
= rl
->heads
; h
; h
= h
->next
) {
636 for (oh
= rl
->heads
; oh
; oh
= oh
->next
) {
637 if (h
->commit
== oh
->commit
)
638 printf ("%s:\n", oh
->name
);
642 for (c
= h
->commit
; c
; c
= p
) {
643 printf ("\t\t%p ", c
);
644 dump_log (stdout
, c
->log
);
646 printf ("\n\t\t...\n");
653 if (p
&& c
->nfiles
> 16) {
657 while (ei
< c
->nfiles
&& pi
< p
->nfiles
) {
661 if (rev_file_later (ef
, pf
)) {
662 fprintf (stdout
, "+ ");
663 dump_number_file (stdout
, ef
->name
, &ef
->number
);
666 fprintf (stdout
, "- ");
667 dump_number_file (stdout
, pf
->name
, &pf
->number
);
670 fprintf (stdout
, "\n");
676 while (ei
< c
->nfiles
) {
678 fprintf (stdout
, "+ ");
679 dump_number_file (stdout
, ef
->name
, &ef
->number
);
681 fprintf (stdout
, "\n");
683 while (pi
< p
->nfiles
) {
685 fprintf (stdout
, "- ");
686 dump_number_file (stdout
, pf
->name
, &pf
->number
);
688 fprintf (stdout
, "\n");
691 for (i
= 0; i
< c
->nfiles
; i
++) {
693 dump_number (c
->files
[i
]->name
, &c
->files
[i
]->number
);
701 if (time_compare (c
->date
, 1079499163) <= 0) {
702 printf ("\t\t...\n");
715 strcommon (char *a
, char *b
)
729 typedef struct _rev_filename
{
730 struct _rev_filename
*next
;
734 #define STATUS stdout
735 #define PROGRESS_LEN 20
736 static int load_current_file
, load_total_files
;
738 static void load_status (char *name
)
740 int spot
= load_current_file
* PROGRESS_LEN
/ load_total_files
;
744 if (rev_mode
== ExecuteGraph
)
747 if (l
> 35) name
+= l
- 35;
749 fprintf (STATUS
, "Load: %35.35s ", name
);
750 for (s
= 0; s
< PROGRESS_LEN
+ 1; s
++)
751 putc (s
== spot
? '*' : '.', STATUS
);
752 fprintf (STATUS
, " %5d of %5d\n", load_current_file
, load_total_files
);
756 static void load_status_next (void)
758 if (rev_mode
== ExecuteGraph
)
760 fprintf (STATUS
, "\n");
764 int commit_time_window
= 60;
765 static int obj_pack_time
= 0;
768 main (int argc
, char **argv
)
770 rev_filename
*fn_head
, **fn_tail
= &fn_head
, *fn
;
771 rev_list
*head
, **tail
= &head
;
774 char name
[10240], *last
= NULL
;
779 rev_list
*pack_start
= NULL
;
780 int pack_objcount
= 0;
783 static struct option options
[] = {
784 { "help", 0, 0, 'h' },
785 { "version", 0, 0, 'V' },
786 { "commit-time-window", 1, 0, 'w' },
787 { "log-command", 1, 0, 'l' },
788 { "autopack", 1, 0, 'p' },
790 int c
= getopt_long(argc
, argv
, "+hVw:l:p:", options
, NULL
);
795 printf("Usage: parsecvs [OPTIONS] [FILE]...\n"
796 "Parse RCS files and populate git repository.\n\n"
797 "Mandatory arguments to long options are mandatory for short options too.\n"
798 " -h --help This help\n"
799 " -l --log-command=COMMAND Call COMMAND to handle changelogs\n"
800 " -p --autopack=NUM Auto-pack for every NUM objects. 0 disables.\n"
802 " -v --version Print version\n"
803 " -w --commit-time-window=WINDOW Time window for commits\n\n"
804 "Example: find -name '*,v' | parsecvs -l edit-change-log -p 1024\n");
807 log_command
= strdup (optarg
);
810 obj_pack_time
= atoi (optarg
);
813 printf("parsecvs version 0.1\n"
815 "Copyright (c) 2006 Keith Packard <keithp@keithp.com>.\n"
816 "This is free software; see the source for copying conditions. There is NO\n"
817 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
820 commit_time_window
= atoi (optarg
);
822 default: /* error message already emitted */
823 fprintf(stderr
, "Try `%s --help' for more information.\n", argv
[0]);
828 argv
[optind
-1] = argv
[0];
832 /* force times using mktime to be interpreted in UTC */
833 setenv ("TZ", "UTC", 1);
834 time_now
= time (NULL
);
839 if (fgets (name
, sizeof (name
) - 1, stdin
) == NULL
)
842 if (name
[l
-1] == '\n')
850 fn
= calloc (1, sizeof (rev_filename
));
851 fn
->file
= atom (file
);
855 c
= strcommon (fn
->file
, last
);
858 } else if (strip
< 0) {
862 for (i
= 0; i
< strlen (fn
->file
); i
++)
863 if (fn
->file
[i
] == '/')
869 if (git_system ("git init --shared") != 0)
871 load_total_files
= nfile
;
872 load_current_file
= 0;
877 fn_head
= fn_head
->next
;
879 load_status (fn
->file
+ strip
);
880 rl
= rev_list_file (fn
->file
, &nversions
);
886 if (rev_mode
== ExecuteGit
&& obj_pack_time
)
889 * Pack objects on occasion to reduce .git directory
894 pack_objcount
+= nversions
;
895 if (pack_objcount
> obj_pack_time
)
897 git_rev_list_pack (pack_start
, strip
);
905 if (rev_mode
== ExecuteGit
&& pack_objcount
&& obj_pack_time
)
906 git_rev_list_pack (pack_start
, strip
);
909 rl
= rev_list_merge (head
);
913 dump_rev_graph (rl
, NULL
);
919 git_rev_list_commit (rl
, strip
);
924 rev_list_free (rl
, 0);
928 rev_list_free (rl
, 1);
934 rev_commit_cleanup ();
935 git_free_author_map ();