Merge branch 'git-describe'
[TortoiseGit.git] / ext / gitdll / gitdll.c
blobab64616b12c0055f8c1943fc6adac1906010f21f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2014 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // gitdll.cpp : Defines the exported functions for the DLL application.
22 #include "stdafx.h"
23 #include "../build/libgit-defines.h"
24 #include "git-compat-util.h"
25 #include "gitdll.h"
26 #include "cache.h"
27 #include "commit.h"
28 #include "diff.h"
29 #include "revision.h"
30 #include "diffcore.h"
31 #include "dir.h"
32 #include "builtin.h"
33 #include "exec_cmd.h"
34 #include "cache.h"
35 #include "quote.h"
36 #include "run-command.h"
37 #include "mailmap.h"
39 extern char g_last_error[];
40 const char * g_prefix;
42 extern void die_dll(const char *err, va_list params);
43 extern int die_is_recursing_dll();
45 void dll_entry()
47 set_die_routine(die_dll);
48 set_die_is_recursing_routine(die_is_recursing_dll);
51 int git_get_sha1(const char *name, GIT_HASH sha1)
53 return get_sha1(name,sha1);
56 static int convert_slash(char * path)
58 while(*path)
60 if(*path == '\\' )
61 *path = '/';
62 path++;
64 return 0;
67 int git_init()
69 char path[MAX_PATH+1];
70 int ret;
71 size_t homesize;
73 _fmode = _O_BINARY;
74 _setmode(_fileno(stdin), _O_BINARY);
75 _setmode(_fileno(stdout), _O_BINARY);
76 _setmode(_fileno(stderr), _O_BINARY);
78 // set HOME if not set already
79 getenv_s(&homesize, NULL, 0, "HOME");
80 if (!homesize)
82 _wputenv_s(L"HOME", wget_windows_home_directory());
84 GetModuleFileName(NULL, path, MAX_PATH);
85 convert_slash(path);
87 git_extract_argv0_path(path);
88 g_prefix = setup_git_directory();
89 ret = git_config(git_default_config, NULL);
91 if (!homesize)
93 _putenv_s("HOME","");/* clear home evironment to avoid affact third part software*/
96 return ret;
99 static int git_parse_commit_author(struct GIT_COMMIT_AUTHOR *author, char *pbuff)
101 char *end;
103 author->Name=pbuff;
104 end=strchr(pbuff,'<');
105 if( end == 0)
107 return -1;
109 author->NameSize = end - pbuff - 1;
111 pbuff = end +1;
112 end = strchr(pbuff, '>');
113 if( end == 0)
114 return -1;
116 author->Email = pbuff ;
117 author->EmailSize = end - pbuff;
119 pbuff = end + 2;
121 author->Date = atol(pbuff);
122 end = strchr(pbuff, ' ');
123 if( end == 0 )
124 return -1;
126 pbuff=end;
127 author->TimeZone = atol(pbuff);
129 return 0;
132 int git_parse_commit(GIT_COMMIT *commit)
134 int ret = 0;
135 char *pbuf;
136 char *end;
137 struct commit *p;
139 p= (struct commit *)commit->m_pGitCommit;
141 memcpy(commit->m_hash,p->object.sha1,GIT_HASH_SIZE);
143 commit->m_Encode = NULL;
144 commit->m_EncodeSize = 0;
146 if(p->buffer == NULL)
147 return -3;
149 pbuf = p->buffer;
150 while(pbuf)
152 if (strncmp(pbuf, "author", 6) == 0)
154 ret = git_parse_commit_author(&commit->m_Author,pbuf + 7);
155 if(ret)
156 return -4;
158 else if (strncmp(pbuf, "committer", 9) == 0)
160 ret = git_parse_commit_author(&commit->m_Committer,pbuf + 10);
161 if(ret)
162 return -5;
164 pbuf = strchr(pbuf,'\n');
165 if(pbuf == NULL)
166 return -6;
168 else if (strncmp(pbuf, "encoding", 8) == 0)
170 pbuf += 9;
171 commit->m_Encode=pbuf;
172 end = strchr(pbuf,'\n');
173 commit->m_EncodeSize=end -pbuf;
176 // the headers end after the first empty line
177 else if (*pbuf == '\n')
179 pbuf++;
181 commit->m_Subject=pbuf;
182 end = strchr(pbuf,'\n');
183 if( end == 0)
184 commit->m_SubjectSize = strlen(pbuf);
185 else
187 commit->m_SubjectSize = end - pbuf;
188 pbuf = end +1;
189 commit->m_Body = pbuf;
190 commit->m_BodySize = strlen(pbuf);
191 return 0;
195 pbuf = strchr(pbuf,'\n');
196 if(pbuf)
197 pbuf ++;
199 return 0;
202 int git_get_commit_from_hash(GIT_COMMIT *commit, GIT_HASH hash)
204 int ret = 0;
206 struct commit *p;
208 if (commit == NULL)
209 return -1;
211 memset(commit,0,sizeof(GIT_COMMIT));
213 commit->m_pGitCommit = p = lookup_commit(hash);
215 if(p == NULL)
216 return -1;
218 ret = parse_commit(p);
219 if( ret )
220 return ret;
222 return git_parse_commit(commit);
225 int git_get_commit_first_parent(GIT_COMMIT *commit,GIT_COMMIT_LIST *list)
227 struct commit *p = commit->m_pGitCommit;
229 if(list == NULL)
230 return -1;
232 *list = (GIT_COMMIT_LIST*)p->parents;
233 return 0;
235 int git_get_commit_next_parent(GIT_COMMIT_LIST *list, GIT_HASH hash)
237 struct commit_list *l;
238 if (list == NULL)
239 return -1;
241 l = *(struct commit_list **)list;
242 if (l == NULL)
243 return -1;
245 if(hash)
246 memcpy(hash, l->item->object.sha1, GIT_HASH_SIZE);
248 *list = (GIT_COMMIT_LIST *)l->next;
249 return 0;
254 int git_free_commit(GIT_COMMIT *commit)
256 struct commit *p = commit->m_pGitCommit;
258 if( p->parents)
259 free_commit_list(p->parents);
261 if( p->buffer )
263 free(p->buffer);
264 p->buffer=NULL;
265 p->object.parsed=0;
266 p->parents=0;
267 p->tree=0;
269 memset(commit,0,sizeof(GIT_COMMIT));
270 return 0;
273 char **strtoargv(char *arg, int *size)
275 int count=0;
276 char *p=arg;
277 char **argv;
279 int i=0;
280 while(*p)
282 if(*p == '\\')
283 *p='/';
284 p++;
286 p=arg;
288 while(*p)
290 if(*p == ' ')
291 count ++;
292 p++;
295 argv=malloc(strlen(arg)+1 + (count +2)*sizeof(void*));
296 p=(char*)(argv+count+2);
298 while(*arg)
300 if(*arg != ' ')
302 char space=' ';
303 argv[i]=p;
305 while(*arg)
307 if(*arg == '"')
309 arg++;
310 if(space == ' ')
311 space = '"';
312 else
313 space = ' ';
315 if((*arg == space) || (*arg == 0))
316 break;
318 *p++ = *arg++;
320 i++;
321 *p++=0;
323 if(*arg == 0)
324 break;
325 arg++;
327 argv[i]=NULL;
328 *size = i;
329 return argv;
331 int git_open_log(GIT_LOG * handle, char * arg)
333 struct rev_info *p_Rev;
334 char ** argv=0;
335 int argc=0;
336 unsigned int i=0;
337 struct setup_revision_opt opt;
339 /* clear flags */
340 unsigned int obj_size = get_max_object_index();
341 for(i =0; i<obj_size; i++)
343 struct object *ob= get_indexed_object(i);
344 if(ob)
345 ob->flags=0;
348 if(arg != NULL)
349 argv = strtoargv(arg,&argc);
351 if (!argv)
352 return -1;
354 p_Rev = malloc(sizeof(struct rev_info));
355 if (p_Rev == NULL)
357 free(argv);
358 return -1;
361 memset(p_Rev,0,sizeof(struct rev_info));
363 invalidate_ref_cache(NULL);
365 init_revisions(p_Rev, g_prefix);
366 p_Rev->diff = 1;
368 memset(&opt, 0, sizeof(opt));
369 opt.def = "HEAD";
371 cmd_log_init(argc, argv, g_prefix,p_Rev,&opt);
373 p_Rev->pPrivate = argv;
374 *handle = p_Rev;
375 return 0;
378 int git_get_log_firstcommit(GIT_LOG handle)
380 return prepare_revision_walk(handle);
383 int git_get_log_estimate_commit_count(GIT_LOG handle)
385 struct rev_info *p_Rev;
386 p_Rev=(struct rev_info *)handle;
388 return estimate_commit_count(p_Rev, p_Rev->commits);
391 int git_get_log_nextcommit(GIT_LOG handle, GIT_COMMIT *commit, int follow)
393 int ret =0;
395 if(commit == NULL)
396 return -1;
398 memset(commit, 0, sizeof(GIT_COMMIT));
400 commit->m_pGitCommit = get_revision(handle);
401 if( commit->m_pGitCommit == NULL)
402 return -2;
404 if (follow && !log_tree_commit(handle, commit->m_pGitCommit))
406 commit->m_ignore = 1;
407 return 0;
409 commit->m_ignore = 0;
411 ret=git_parse_commit(commit);
412 if(ret)
413 return ret;
415 return 0;
418 struct notes_tree **display_notes_trees;
419 int git_close_log(GIT_LOG handle)
421 if(handle)
423 struct rev_info *p_Rev;
424 p_Rev=(struct rev_info *)handle;
425 if(p_Rev->pPrivate)
426 free(p_Rev->pPrivate);
427 free(handle);
429 free_all_pack();
431 if (display_notes_trees)
432 free_notes(*display_notes_trees);
433 display_notes_trees = 0;
434 return 0;
437 int git_open_diff(GIT_DIFF *diff, char * arg)
439 struct rev_info *p_Rev;
440 char ** argv=0;
441 int argc=0;
443 if(arg != NULL)
444 argv = strtoargv(arg,&argc);
446 p_Rev = malloc(sizeof(struct rev_info));
447 memset(p_Rev,0,sizeof(struct rev_info));
449 p_Rev->pPrivate = argv;
450 *diff = (GIT_DIFF)p_Rev;
452 init_revisions(p_Rev, g_prefix);
453 git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
454 p_Rev->abbrev = 0;
455 p_Rev->diff = 1;
456 argc = setup_revisions(argc, argv, p_Rev, NULL);
458 return 0;
460 int git_close_diff(GIT_DIFF handle)
462 git_diff_flush(handle);
463 if(handle)
465 struct rev_info *p_Rev;
466 p_Rev=(struct rev_info *)handle;
467 if(p_Rev->pPrivate)
468 free(p_Rev->pPrivate);
469 free(handle);
471 return 0;
473 int git_diff_flush(GIT_DIFF diff)
475 struct diff_queue_struct *q = &diff_queued_diff;
476 struct rev_info *p_Rev;
477 int i;
478 p_Rev = (struct rev_info *)diff;
480 if(q->nr == 0)
481 return 0;
483 for (i = 0; i < q->nr; i++)
484 diff_free_filepair(q->queue[i]);
486 if(q->queue)
488 free(q->queue);
489 q->queue = NULL;
490 q->nr = q->alloc = 0;
493 if (p_Rev->diffopt.close_file)
494 fclose(p_Rev->diffopt.file);
496 free_diffstat_info(&p_Rev->diffstat);
497 return 0;
500 int git_root_diff(GIT_DIFF diff, GIT_HASH hash,GIT_FILE *file, int *count, int isstat)
502 int ret;
503 struct rev_info *p_Rev;
504 int i;
505 struct diff_queue_struct *q = &diff_queued_diff;
507 p_Rev = (struct rev_info *)diff;
509 ret=diff_root_tree_sha1(hash, "", &p_Rev->diffopt);
511 if(ret)
512 return ret;
514 if(isstat)
516 diffcore_std(&p_Rev->diffopt);
518 memset(&p_Rev->diffstat, 0, sizeof(struct diffstat_t));
519 for (i = 0; i < q->nr; i++) {
520 struct diff_filepair *p = q->queue[i];
521 //if (check_pair_status(p))
522 diff_flush_stat(p, &p_Rev->diffopt, &p_Rev->diffstat);
525 if(file)
526 *file = q;
527 if(count)
528 *count = q->nr;
530 return 0;
533 int git_do_diff(GIT_DIFF diff, GIT_HASH hash1, GIT_HASH hash2, GIT_FILE * file, int *count,int isstat)
535 struct rev_info *p_Rev;
536 int ret;
537 int i;
538 struct diff_queue_struct *q = &diff_queued_diff;
540 p_Rev = (struct rev_info *)diff;
542 ret = diff_tree_sha1(hash1,hash2,"",&p_Rev->diffopt);
543 if( ret )
545 free_all_pack();
546 return ret;
549 if(isstat)
551 diffcore_std(&p_Rev->diffopt);
552 memset(&p_Rev->diffstat, 0, sizeof(struct diffstat_t));
553 for (i = 0; i < q->nr; i++) {
554 struct diff_filepair *p = q->queue[i];
555 //if (check_pair_status(p))
556 diff_flush_stat(p, &p_Rev->diffopt, &p_Rev->diffstat);
559 free_all_pack();
560 if(file)
561 *file = q;
562 if(count)
563 *count = q->nr;
564 return 0;
567 int git_get_diff_file(GIT_DIFF diff,GIT_FILE file,int i, char **newname, char ** oldname, int *status, int *IsBin, int *inc, int *dec)
569 struct diff_queue_struct *q = &diff_queued_diff;
570 struct rev_info *p_Rev;
571 p_Rev = (struct rev_info *)diff;
573 q = (struct diff_queue_struct *)file;
574 if(file == 0)
575 return -1;
576 if(i>=q->nr)
577 return -1;
579 assert(newname && oldname && status);
581 *newname = q->queue[i]->two->path;
582 *oldname = q->queue[i]->one->path;
583 *status = q->queue[i]->status;
585 if(p_Rev->diffstat.files)
587 int j;
588 for(j=0;j<p_Rev->diffstat.nr;j++)
590 if(strcmp(*newname,p_Rev->diffstat.files[j]->name)==0)
591 break;
593 if( j== p_Rev->diffstat.nr)
595 *IsBin=1;
596 *inc=0;
597 *dec=0;
598 return 0;
600 if(IsBin)
601 *IsBin = p_Rev->diffstat.files[j]->is_binary;
602 if(inc)
603 *inc = p_Rev->diffstat.files[j]->added;
604 if(dec)
605 *dec = p_Rev->diffstat.files[j]->deleted;
606 }else
608 *IsBin=1;
609 *inc=0;
610 *dec=0;
613 return 0;
616 int git_read_tree(GIT_HASH hash,read_tree_fn_t fn, void *context)
618 struct tree * root;
619 int ret;
620 reprepare_packed_git();
621 root = parse_tree_indirect(hash);
623 if (!root)
625 free_all_pack();
626 return -1;
628 ret = read_tree_recursive(root, NULL, 0, 0, NULL, fn, context);
629 free_all_pack();
630 return ret;
633 int git_add_exclude(const char *string, const char *base,
634 int baselen, struct exclude_list *which, int lineno)
636 add_exclude(string, base, baselen, which, lineno);
637 return 0;
640 int git_create_exclude_list(EXCLUDE_LIST *which)
642 *which = malloc(sizeof(struct exclude_list));
643 memset(*which,0,sizeof(struct exclude_list));
644 return 0;
647 int git_free_exclude_list(EXCLUDE_LIST which)
649 int i=0;
650 struct exclude_list *p = (struct exclude_list *) which;
652 for(i=0; i<p->nr;i++)
654 free(p->excludes[i]);
656 free(p->excludes);
657 free(p);
658 return 0;
661 int git_check_excluded_1(const char *pathname,
662 int pathlen, const char *basename, int *dtype,
663 EXCLUDE_LIST el)
665 return is_excluded_from_list(pathname, pathlen, basename, dtype, el);
668 int git_get_notes(GIT_HASH hash, char **p_note)
670 struct strbuf sb;
671 size_t size;
672 strbuf_init(&sb,0);
673 format_display_notes(hash, &sb, "utf-8", 1);
674 *p_note = strbuf_detach(&sb,&size);
676 return 0;
679 struct cmd_struct {
680 const char *cmd;
681 int (*fn)(int, const char **, const char *);
682 int option;
685 #define RUN_SETUP (1<<0)
686 #define USE_PAGER (1<<1)
688 * require working tree to be present -- anything uses this needs
689 * RUN_SETUP for reading from the configuration file.
691 #define NEED_WORK_TREE (1<<2)
693 const char git_usage_string[] =
694 "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
695 " [-p|--paginate|--no-pager] [--no-replace-objects]\n"
696 " [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
697 " [-c name=value] [--help]\n"
698 " COMMAND [ARGS]";
700 const char git_more_info_string[] =
701 "See 'git help COMMAND' for more information on a specific command.";
703 static struct cmd_struct commands[] = {
704 { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
705 { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
706 { "annotate", cmd_annotate, RUN_SETUP },
707 { "apply", cmd_apply },
708 { "archive", cmd_archive },
709 { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
710 { "blame", cmd_blame, RUN_SETUP },
711 { "branch", cmd_branch, RUN_SETUP },
712 { "bundle", cmd_bundle },
713 { "cat-file", cmd_cat_file, RUN_SETUP },
714 { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
715 { "checkout-index", cmd_checkout_index,
716 RUN_SETUP | NEED_WORK_TREE},
717 { "check-ref-format", cmd_check_ref_format },
718 { "check-attr", cmd_check_attr, RUN_SETUP },
719 { "cherry", cmd_cherry, RUN_SETUP },
720 { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
721 { "clone", cmd_clone },
722 { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
723 { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
724 { "commit-tree", cmd_commit_tree, RUN_SETUP },
725 { "config", cmd_config },
726 { "count-objects", cmd_count_objects, RUN_SETUP },
727 { "describe", cmd_describe, RUN_SETUP },
728 { "diff", cmd_diff },
729 { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
730 { "diff-index", cmd_diff_index, RUN_SETUP },
731 { "diff-tree", cmd_diff_tree, RUN_SETUP },
732 { "fast-export", cmd_fast_export, RUN_SETUP },
733 { "fetch", cmd_fetch, RUN_SETUP },
734 { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
735 { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
736 { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
737 { "format-patch", cmd_format_patch, RUN_SETUP },
738 { "fsck", cmd_fsck, RUN_SETUP },
739 { "fsck-objects", cmd_fsck, RUN_SETUP },
740 { "gc", cmd_gc, RUN_SETUP },
741 { "get-tar-commit-id", cmd_get_tar_commit_id },
742 { "grep", cmd_grep },
743 { "hash-object", cmd_hash_object },
744 { "help", cmd_help },
745 { "index-pack", cmd_index_pack },
746 { "init", cmd_init_db },
747 { "init-db", cmd_init_db },
748 { "log", cmd_log, RUN_SETUP },
749 { "ls-files", cmd_ls_files, RUN_SETUP },
750 { "ls-tree", cmd_ls_tree, RUN_SETUP },
751 { "ls-remote", cmd_ls_remote },
752 { "mailinfo", cmd_mailinfo },
753 { "mailsplit", cmd_mailsplit },
754 { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
755 { "merge-base", cmd_merge_base, RUN_SETUP },
756 { "merge-file", cmd_merge_file },
757 { "merge-index", cmd_merge_index, RUN_SETUP },
758 { "merge-ours", cmd_merge_ours, RUN_SETUP },
759 { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
760 { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
761 { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
762 { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
763 { "merge-tree", cmd_merge_tree, RUN_SETUP },
764 { "mktag", cmd_mktag, RUN_SETUP },
765 { "mktree", cmd_mktree, RUN_SETUP },
766 { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
767 { "name-rev", cmd_name_rev, RUN_SETUP },
768 { "notes", cmd_notes, RUN_SETUP },
769 { "pack-objects", cmd_pack_objects, RUN_SETUP },
770 { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
771 { "patch-id", cmd_patch_id },
772 { "pickaxe", cmd_blame, RUN_SETUP },
773 { "prune", cmd_prune, RUN_SETUP },
774 { "prune-packed", cmd_prune_packed, RUN_SETUP },
775 { "push", cmd_push, RUN_SETUP },
776 { "read-tree", cmd_read_tree, RUN_SETUP },
777 { "receive-pack", cmd_receive_pack },
778 { "reflog", cmd_reflog, RUN_SETUP },
779 { "remote", cmd_remote, RUN_SETUP },
780 { "replace", cmd_replace, RUN_SETUP },
781 { "rerere", cmd_rerere, RUN_SETUP },
782 { "reset", cmd_reset, RUN_SETUP },
783 { "rev-list", cmd_rev_list, RUN_SETUP },
784 { "rev-parse", cmd_rev_parse },
785 { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
786 { "rm", cmd_rm, RUN_SETUP },
787 { "send-pack", cmd_send_pack, RUN_SETUP },
788 { "shortlog", cmd_shortlog, USE_PAGER },
789 { "show-branch", cmd_show_branch, RUN_SETUP },
790 { "show", cmd_show, RUN_SETUP },
791 { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
792 { "stripspace", cmd_stripspace },
793 { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
794 { "tag", cmd_tag, RUN_SETUP },
795 { "unpack-file", cmd_unpack_file, RUN_SETUP },
796 { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
797 { "update-index", cmd_update_index, RUN_SETUP },
798 { "update-ref", cmd_update_ref, RUN_SETUP },
799 { "update-server-info", cmd_update_server_info, RUN_SETUP },
800 { "upload-archive", cmd_upload_archive },
801 { "var", cmd_var },
802 { "verify-tag", cmd_verify_tag, RUN_SETUP },
803 { "version", cmd_version },
804 { "whatchanged", cmd_whatchanged, RUN_SETUP },
805 { "write-tree", cmd_write_tree, RUN_SETUP },
806 { "verify-pack", cmd_verify_pack },
807 { "show-ref", cmd_show_ref, RUN_SETUP },
808 { "pack-refs", cmd_pack_refs, RUN_SETUP },
811 int is_builtin(const char *s)
813 int i;
814 for (i = 0; i < ARRAY_SIZE(commands); i++) {
815 struct cmd_struct *p = commands+i;
816 if (!strcmp(s, p->cmd))
817 return 1;
819 return 0;
822 int git_run_cmd(char *cmd, char *arg)
825 int i=0;
826 char ** argv=0;
827 int argc=0;
829 git_init();
831 for(i=0;i< sizeof(commands) / sizeof(struct cmd_struct);i++)
833 if(strcmp(cmd,commands[i].cmd)==0)
835 int ret;
836 if(arg != NULL)
837 argv = strtoargv(arg,&argc);
839 ret = commands[i].fn(argc, argv, NULL);
841 if(argv)
842 free(argv);
844 discard_cache();
845 free_all_pack();
847 return ret;
852 return -1;
855 int git_for_each_ref_in(const char * refname, each_ref_fn fn, void * data)
857 int ret;
858 invalidate_ref_cache(NULL);
859 ret = for_each_ref_in(refname, fn, data);
860 free_all_pack();
861 return ret;
864 const char *git_resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
866 invalidate_ref_cache(NULL);
867 return resolve_ref_unsafe(ref,sha1,reading, flag);
869 int git_for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
871 return for_each_reflog_ent(ref,fn,cb_data);
874 int git_deref_tag(const unsigned char *tagsha1, GIT_HASH refhash)
876 struct object *obj = NULL;
877 obj = parse_object(tagsha1);
878 if (!obj)
879 return -1;
881 if (obj->type == OBJ_TAG)
883 obj = deref_tag(obj, "", 0);
884 if (!obj)
885 return -1;
887 memcpy(refhash, obj->sha1, sizeof(GIT_HASH));
888 return 0;
891 return -1;
894 static int update_some(const unsigned char *sha1, const char *base, int baselen,
895 const char *pathname, unsigned mode, int stage, void *context)
897 struct cache_entry *ce;
898 UNREFERENCED_PARAMETER(stage);
900 ce = (struct cache_entry *)context;
902 if (S_ISDIR(mode))
903 return READ_TREE_RECURSIVE;
905 hashcpy(ce->sha1, sha1);
906 memcpy(ce->name, base, baselen);
907 memcpy(ce->name + baselen, pathname, strlen(pathname));
908 ce->ce_flags = create_ce_flags(strlen(pathname) + baselen);
909 ce->ce_mode = create_ce_mode(mode);
911 return 0;
914 int git_checkout_file(const char *ref, const char *path, const char *outputpath)
916 struct cache_entry *ce;
917 int ret;
918 GIT_HASH sha1;
919 struct tree * root;
920 struct checkout state;
921 struct pathspec pathspec;
922 const char *matchbuf[1];
923 ret = get_sha1(ref, sha1);
924 if(ret)
925 return ret;
927 reprepare_packed_git();
928 root = parse_tree_indirect(sha1);
930 if(!root)
932 free_all_pack();
933 return -1;
936 ce = xcalloc(1, cache_entry_size(strlen(path)));
938 matchbuf[0] = NULL;
939 parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, PATHSPEC_PREFER_CWD, path, matchbuf);
940 pathspec.items[0].nowildcard_len = pathspec.items[0].len;
941 ret = read_tree_recursive(root, "", 0, 0, &pathspec, update_some, ce);
942 free_pathspec(&pathspec);
944 if(ret)
946 free_all_pack();
947 free(ce);
948 return ret;
950 memset(&state, 0, sizeof(state));
951 state.force = 1;
952 state.refresh_cache = 0;
954 ret = write_entry(ce, outputpath, &state, 0);
955 free_all_pack();
956 free(ce);
957 return ret;
959 struct config_buf
961 char *buf;
962 const char *key;
963 size_t size;
964 int seen;
967 static int get_config(const char *key_, const char *value_, void *cb)
969 struct config_buf *buf;
970 buf=(struct config_buf*)cb;
971 if(strcmp(key_, buf->key))
972 return 0;
974 if (value_)
975 strncpy(buf->buf,value_,buf->size);
976 else
978 buf->buf[0] = 't';
979 buf->buf[1] = 'r';
980 buf->buf[2] = 'u';
981 buf->buf[3] = 'e';
982 buf->buf[4] = 0;
984 buf->seen = 1;
985 return 0;
989 // wchar_t wrapper for git_etc_gitconfig()
990 const wchar_t *wget_msysgit_etc(void)
992 static const wchar_t *etc_gitconfig = NULL;
993 wchar_t wpointer[MAX_PATH];
995 if (etc_gitconfig)
996 return etc_gitconfig;
998 if (xutftowcs_path(wpointer, git_etc_gitconfig()) < 0)
999 return NULL;
1001 etc_gitconfig = _wcsdup(wpointer);
1003 return etc_gitconfig;
1006 int git_get_config(const char *key, char *buffer, int size)
1008 char *local, *global, *globalxdg;
1009 const char *home, *system;
1010 struct config_buf buf;
1012 buf.buf=buffer;
1013 buf.size=size;
1014 buf.seen = 0;
1015 buf.key = key;
1017 home = get_windows_home_directory();
1018 if (home)
1020 global = xstrdup(mkpath("%s/.gitconfig", home));
1021 globalxdg = xstrdup(mkpath("%s/.config/git/config", home));
1023 else
1025 global = NULL;
1026 globalxdg = NULL;
1029 system = git_etc_gitconfig();
1031 local = git_pathdup("config");
1033 if ( !buf.seen)
1034 git_config_with_options(get_config, &buf, local, NULL, 1);
1035 if (!buf.seen && global)
1036 git_config_with_options(get_config, &buf, global, NULL, 1);
1037 if (!buf.seen && globalxdg)
1038 git_config_with_options(get_config, &buf, globalxdg, NULL, 1);
1039 if (!buf.seen && system)
1040 git_config_with_options(get_config, &buf, system, NULL, 1);
1042 if(local)
1043 free(local);
1044 if(global)
1045 free(global);
1046 if (globalxdg)
1047 free(globalxdg);
1049 return !buf.seen;
1052 // taken from msysgit: compat/mingw.c
1053 const char *get_windows_home_directory(void)
1055 static const char *home_directory = NULL;
1056 struct strbuf buf = STRBUF_INIT;
1058 if (home_directory)
1059 return home_directory;
1061 home_directory = getenv("HOME");
1062 if (home_directory && *home_directory)
1063 return home_directory;
1065 strbuf_addf(&buf, "%s%s", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
1066 home_directory = strbuf_detach(&buf, NULL);
1068 return home_directory;
1071 // wchar_t wrapper for get_windows_home_directory()
1072 const wchar_t *wget_windows_home_directory(void)
1074 static const wchar_t *home_directory = NULL;
1075 wchar_t wpointer[MAX_PATH];
1077 if (home_directory)
1078 return home_directory;
1080 if (xutftowcs_path(wpointer, get_windows_home_directory()) < 0)
1081 return NULL;
1083 home_directory = _wcsdup(wpointer);
1085 return home_directory;
1088 int get_set_config(const char *key, const char *value, CONFIG_TYPE type)
1090 char * config_exclusive_filename = NULL;
1092 switch(type)
1094 case CONFIG_LOCAL:
1095 config_exclusive_filename = git_pathdup("config");
1096 break;
1097 case CONFIG_GLOBAL:
1098 case CONFIG_XDGGLOBAL:
1100 const char *home = get_windows_home_directory();
1101 if (home)
1103 if (type == CONFIG_GLOBAL)
1104 config_exclusive_filename = xstrdup(mkpath("%s/.gitconfig", home));
1105 else
1106 config_exclusive_filename = xstrdup(mkpath("%s/.config/git/config", home));
1109 break;
1112 if(!config_exclusive_filename)
1113 return -1;
1115 return git_config_set_multivar_in_file(config_exclusive_filename, key, value, NULL, 0);
1118 struct mailmap_info {
1119 char *name;
1120 char *email;
1123 struct mailmap_entry {
1124 /* name and email for the simple mail-only case */
1125 char *name;
1126 char *email;
1128 /* name and email for the complex mail and name matching case */
1129 struct string_list namemap;
1132 int git_read_mailmap(GIT_MAILMAP *mailmap)
1134 struct string_list *map;
1135 int result;
1137 if (!mailmap)
1138 return -1;
1140 *mailmap = NULL;
1141 if ((map = (struct string_list *)calloc(1, sizeof(struct string_list))) == NULL)
1142 return -1;
1144 if ((result = read_mailmap(map, NULL)) != 0)
1145 return result;
1147 *mailmap = map;
1148 return 0;
1151 const char * git_get_mailmap_author(GIT_MAILMAP mailmap, const char *email2, void *payload, const char *(*author2_cb)(void *))
1153 struct string_list *map;
1154 int imax, imin = 0;
1156 if (!mailmap)
1157 return NULL;
1159 map = (struct string_list *)mailmap;
1160 imax = map->nr - 1;
1161 while (imax >= imin)
1163 int i = imin + ((imax - imin) / 2);
1164 struct string_list_item *si = (struct string_list_item *)&map->items[i];
1165 struct mailmap_entry *me = (struct mailmap_entry *)si->util;
1166 int comp = strcmp(si->string, email2);
1168 if (!comp)
1170 if (me->namemap.nr)
1172 const char *author2 = author2_cb(payload);
1173 unsigned int j;
1174 for (j = 0; j < me->namemap.nr; ++j)
1176 struct string_list_item *sj = (struct string_list_item *)&me->namemap.items[j];
1177 struct mailmap_info *mi = (struct mailmap_info *)sj->util;
1179 if (!strcmp(sj->string, author2))
1180 return mi->name;
1184 return me->name;
1186 else if (comp < 0)
1187 imin = i + 1;
1188 else
1189 imax = i - 1;
1192 return NULL;
1195 void git_free_mailmap(GIT_MAILMAP mailmap)
1197 if (!mailmap)
1198 return;
1200 clear_mailmap((struct string_list *)mailmap);
1201 free(mailmap);