Prefer to use VS2013 for compiling and testing on AppVeyor
[TortoiseGit.git] / ext / gitdll / gitdll.c
blobb07ec968c0f5ded3f435be259c9803b22fb254a9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - 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 #pragma warning(push)
25 #pragma warning(disable: 4100 4018 4127 4244 4267)
26 #include "git-compat-util.h"
27 #include "gitdll.h"
28 #include "cache.h"
29 #include "commit.h"
30 #include "diff.h"
31 #include "revision.h"
32 #include "diffcore.h"
33 #include "dir.h"
34 #include "builtin.h"
35 #include "exec_cmd.h"
36 #include "cache.h"
37 #include "quote.h"
38 #include "run-command.h"
39 #include "mailmap.h"
40 #pragma warning(pop)
42 extern char g_last_error[];
43 const char * g_prefix;
45 extern void die_dll(const char *err, va_list params);
46 extern int die_is_recursing_dll(void);
48 extern void free_all_pack();
49 extern void reset_git_env();
50 extern void invalidate_ref_cache(const char* submodule);
51 extern void cmd_log_init(int argc, const char** argv, const char* prefix, struct rev_info* rev, struct setup_revision_opt* opt);
52 extern int estimate_commit_count(struct rev_info* rev, struct commit_list* list);
53 extern int log_tree_commit(struct rev_info*, struct commit*);
54 extern int write_entry(struct cache_entry* ce, char* path, const struct checkout* state, int to_tempfile);
55 extern struct object* deref_tag(struct object* o, const char* warn, int warnlen);
56 extern void diff_flush_stat(struct diff_filepair* p, struct diff_options* o, struct diffstat_t* diffstat);
57 extern void free_diffstat_info(struct diffstat_t* diffstat);
58 extern int for_each_reflog_ent(const char* refname, each_reflog_ent_fn fn, void* cb_data);
59 extern int for_each_ref_in(const char* prefix, each_ref_fn fn, void* cb_data);
61 void dll_entry()
63 set_die_routine(die_dll);
64 set_die_is_recursing_routine(die_is_recursing_dll);
67 int git_get_sha1(const char *name, GIT_HASH sha1)
69 return get_sha1(name,sha1);
72 static int convert_slash(char * path)
74 while(*path)
76 if(*path == '\\' )
77 *path = '/';
78 path++;
80 return 0;
83 int git_init()
85 char path[MAX_PATH+1];
86 size_t homesize;
88 _fmode = _O_BINARY;
89 _setmode(_fileno(stdin), _O_BINARY);
90 _setmode(_fileno(stdout), _O_BINARY);
91 _setmode(_fileno(stderr), _O_BINARY);
93 // set HOME if not set already
94 getenv_s(&homesize, NULL, 0, "HOME");
95 if (!homesize)
97 _wputenv_s(L"HOME", wget_windows_home_directory());
99 GetModuleFileName(NULL, path, MAX_PATH);
100 convert_slash(path);
102 git_extract_argv0_path(path);
103 reset_git_env();
104 g_prefix = setup_git_directory();
105 git_config(git_default_config, NULL);
107 if (!homesize)
109 _putenv_s("HOME","");/* clear home evironment to avoid affact third part software*/
112 return 0;
115 static int git_parse_commit_author(struct GIT_COMMIT_AUTHOR* author, const char* pbuff)
117 const char* end;
119 author->Name=pbuff;
120 end=strchr(pbuff,'<');
121 if( end == 0)
123 return -1;
125 author->NameSize = (int)(end - pbuff - 1);
127 pbuff = end +1;
128 end = strchr(pbuff, '>');
129 if( end == 0)
130 return -1;
132 author->Email = pbuff ;
133 author->EmailSize = (int)(end - pbuff);
135 pbuff = end + 2;
137 author->Date = atol(pbuff);
138 end = strchr(pbuff, ' ');
139 if( end == 0 )
140 return -1;
142 pbuff=end;
143 author->TimeZone = atol(pbuff);
145 return 0;
148 int git_parse_commit(GIT_COMMIT *commit)
150 int ret = 0;
151 const char* pbuf;
152 const char* end;
153 struct commit *p;
155 p= (struct commit *)commit->m_pGitCommit;
157 memcpy(commit->m_hash,p->object.sha1,GIT_HASH_SIZE);
159 commit->m_Encode = NULL;
160 commit->m_EncodeSize = 0;
162 get_commit_buffer(commit->m_pGitCommit, NULL);;
163 commit->buffer = detach_commit_buffer(commit->m_pGitCommit, NULL);
165 pbuf = commit->buffer;
166 while(pbuf)
168 if (strncmp(pbuf, "author", 6) == 0)
170 ret = git_parse_commit_author(&commit->m_Author,pbuf + 7);
171 if(ret)
172 return -4;
174 else if (strncmp(pbuf, "committer", 9) == 0)
176 ret = git_parse_commit_author(&commit->m_Committer,pbuf + 10);
177 if(ret)
178 return -5;
180 pbuf = strchr(pbuf,'\n');
181 if(pbuf == NULL)
182 return -6;
184 else if (strncmp(pbuf, "encoding", 8) == 0)
186 pbuf += 9;
187 commit->m_Encode=pbuf;
188 end = strchr(pbuf,'\n');
189 commit->m_EncodeSize= (int)(end -pbuf);
192 // the headers end after the first empty line
193 else if (*pbuf == '\n')
195 pbuf++;
197 commit->m_Subject=pbuf;
198 end = strchr(pbuf,'\n');
199 if( end == 0)
200 commit->m_SubjectSize = (int)strlen(pbuf);
201 else
203 commit->m_SubjectSize = (int)(end - pbuf);
204 pbuf = end +1;
205 commit->m_Body = pbuf;
206 commit->m_BodySize = (int)strlen(pbuf);
207 return 0;
211 pbuf = strchr(pbuf,'\n');
212 if(pbuf)
213 pbuf ++;
215 return 0;
218 int git_get_commit_from_hash(GIT_COMMIT *commit, GIT_HASH hash)
220 int ret = 0;
222 struct commit *p;
224 if (commit == NULL)
225 return -1;
227 memset(commit,0,sizeof(GIT_COMMIT));
229 commit->m_pGitCommit = p = lookup_commit(hash);
231 if(p == NULL)
232 return -1;
234 ret = parse_commit(p);
235 if( ret )
236 return ret;
238 return git_parse_commit(commit);
241 int git_get_commit_first_parent(GIT_COMMIT *commit,GIT_COMMIT_LIST *list)
243 struct commit *p = commit->m_pGitCommit;
245 if(list == NULL)
246 return -1;
248 *list = (GIT_COMMIT_LIST*)p->parents;
249 return 0;
251 int git_get_commit_next_parent(GIT_COMMIT_LIST *list, GIT_HASH hash)
253 struct commit_list *l;
254 if (list == NULL)
255 return -1;
257 l = *(struct commit_list **)list;
258 if (l == NULL)
259 return -1;
261 if(hash)
262 memcpy(hash, l->item->object.sha1, GIT_HASH_SIZE);
264 *list = (GIT_COMMIT_LIST *)l->next;
265 return 0;
270 int git_free_commit(GIT_COMMIT *commit)
272 struct commit *p = commit->m_pGitCommit;
274 if( p->parents)
275 free_commit_list(p->parents);
277 if (p->tree)
278 free_tree_buffer(p->tree);
280 #pragma warning(push)
281 #pragma warning(disable: 4090)
282 if (commit->buffer)
283 free(commit->buffer);
284 #pragma warning(pop)
286 p->object.parsed = 0;
287 p->parents = 0;
288 p->tree = 0;
290 memset(commit,0,sizeof(GIT_COMMIT));
291 return 0;
294 char **strtoargv(char *arg, int *size)
296 int count=0;
297 char *p=arg;
298 char **argv;
300 int i=0;
301 while(*p)
303 if(*p == '\\')
304 *p='/';
305 p++;
307 p=arg;
309 while(*p)
311 if(*p == ' ')
312 count ++;
313 p++;
316 argv=malloc(strlen(arg)+1 + (count +2)*sizeof(void*));
317 p=(char*)(argv+count+2);
319 while(*arg)
321 if(*arg != ' ')
323 char space=' ';
324 argv[i]=p;
326 while(*arg)
328 if(*arg == '"')
330 arg++;
331 if(space == ' ')
332 space = '"';
333 else
334 space = ' ';
336 if((*arg == space) || (*arg == 0))
337 break;
339 *p++ = *arg++;
341 i++;
342 *p++=0;
344 if(*arg == 0)
345 break;
346 arg++;
348 argv[i]=NULL;
349 *size = i;
350 return argv;
352 int git_open_log(GIT_LOG * handle, char * arg)
354 struct rev_info *p_Rev;
355 char ** argv=0;
356 int argc=0;
357 unsigned int i=0;
358 struct setup_revision_opt opt;
360 /* clear flags */
361 unsigned int obj_size = get_max_object_index();
362 for(i =0; i<obj_size; i++)
364 struct object *ob= get_indexed_object(i);
365 if(ob)
367 ob->flags=0;
368 if (ob->parsed && ob->type == OBJ_COMMIT)
370 struct commit* commit = (struct commit*)ob;
371 free_commit_list(commit->parents);
372 commit->parents = NULL;
373 if (commit->tree)
374 free_tree_buffer(commit->tree);
375 commit->tree = NULL;
376 ob->parsed = 0;
381 if(arg != NULL)
382 argv = strtoargv(arg,&argc);
384 if (!argv)
385 return -1;
387 p_Rev = malloc(sizeof(struct rev_info));
388 if (p_Rev == NULL)
390 free(argv);
391 return -1;
394 memset(p_Rev,0,sizeof(struct rev_info));
396 invalidate_ref_cache(NULL);
398 init_revisions(p_Rev, g_prefix);
399 p_Rev->diff = 1;
401 memset(&opt, 0, sizeof(opt));
402 opt.def = "HEAD";
404 cmd_log_init(argc, argv, g_prefix,p_Rev,&opt);
406 p_Rev->pPrivate = argv;
407 *handle = p_Rev;
408 return 0;
411 int git_get_log_firstcommit(GIT_LOG handle)
413 return prepare_revision_walk(handle);
416 int git_get_log_estimate_commit_count(GIT_LOG handle)
418 struct rev_info *p_Rev;
419 p_Rev=(struct rev_info *)handle;
421 return estimate_commit_count(p_Rev, p_Rev->commits);
424 int git_get_log_nextcommit(GIT_LOG handle, GIT_COMMIT *commit, int follow)
426 int ret =0;
428 if(commit == NULL)
429 return -1;
431 memset(commit, 0, sizeof(GIT_COMMIT));
433 commit->m_pGitCommit = get_revision(handle);
434 if( commit->m_pGitCommit == NULL)
435 return -2;
437 if (follow && !log_tree_commit(handle, commit->m_pGitCommit))
439 commit->m_ignore = 1;
440 return 0;
442 commit->m_ignore = 0;
444 ret=git_parse_commit(commit);
445 if(ret)
446 return ret;
448 return 0;
451 struct notes_tree **display_notes_trees;
452 int git_close_log(GIT_LOG handle)
454 if(handle)
456 struct rev_info *p_Rev;
457 p_Rev=(struct rev_info *)handle;
458 if(p_Rev->pPrivate)
459 free(p_Rev->pPrivate);
460 free(handle);
462 free_all_pack();
464 if (display_notes_trees)
465 free_notes(*display_notes_trees);
466 display_notes_trees = 0;
467 return 0;
470 int git_open_diff(GIT_DIFF *diff, char * arg)
472 struct rev_info *p_Rev;
473 char ** argv=0;
474 int argc=0;
476 if(arg != NULL)
477 argv = strtoargv(arg,&argc);
479 p_Rev = malloc(sizeof(struct rev_info));
480 memset(p_Rev,0,sizeof(struct rev_info));
482 p_Rev->pPrivate = argv;
483 *diff = (GIT_DIFF)p_Rev;
485 init_revisions(p_Rev, g_prefix);
486 git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
487 p_Rev->abbrev = 0;
488 p_Rev->diff = 1;
489 argc = setup_revisions(argc, argv, p_Rev, NULL);
491 return 0;
493 int git_close_diff(GIT_DIFF handle)
495 git_diff_flush(handle);
496 if(handle)
498 struct rev_info *p_Rev;
499 p_Rev=(struct rev_info *)handle;
500 if(p_Rev->pPrivate)
501 free(p_Rev->pPrivate);
502 free(handle);
504 return 0;
506 int git_diff_flush(GIT_DIFF diff)
508 struct diff_queue_struct *q = &diff_queued_diff;
509 struct rev_info *p_Rev;
510 int i;
511 p_Rev = (struct rev_info *)diff;
513 if(q->nr == 0)
514 return 0;
516 for (i = 0; i < q->nr; i++)
517 diff_free_filepair(q->queue[i]);
519 if(q->queue)
521 free(q->queue);
522 q->queue = NULL;
523 q->nr = q->alloc = 0;
526 if (p_Rev->diffopt.close_file)
527 fclose(p_Rev->diffopt.file);
529 free_diffstat_info(&p_Rev->diffstat);
530 return 0;
533 int git_root_diff(GIT_DIFF diff, GIT_HASH hash,GIT_FILE *file, int *count, int isstat)
535 int ret;
536 struct rev_info *p_Rev;
537 int i;
538 struct diff_queue_struct *q = &diff_queued_diff;
540 p_Rev = (struct rev_info *)diff;
542 ret=diff_root_tree_sha1(hash, "", &p_Rev->diffopt);
544 if(ret)
545 return ret;
547 if(isstat)
549 diffcore_std(&p_Rev->diffopt);
551 memset(&p_Rev->diffstat, 0, sizeof(struct diffstat_t));
552 for (i = 0; i < q->nr; i++) {
553 struct diff_filepair *p = q->queue[i];
554 //if (check_pair_status(p))
555 diff_flush_stat(p, &p_Rev->diffopt, &p_Rev->diffstat);
558 if(file)
559 *file = q;
560 if(count)
561 *count = q->nr;
563 return 0;
566 int git_do_diff(GIT_DIFF diff, GIT_HASH hash1, GIT_HASH hash2, GIT_FILE * file, int *count,int isstat)
568 struct rev_info *p_Rev;
569 int ret;
570 int i;
571 struct diff_queue_struct *q = &diff_queued_diff;
573 p_Rev = (struct rev_info *)diff;
575 ret = diff_tree_sha1(hash1,hash2,"",&p_Rev->diffopt);
576 if( ret )
578 free_all_pack();
579 return ret;
582 if(isstat)
584 diffcore_std(&p_Rev->diffopt);
585 memset(&p_Rev->diffstat, 0, sizeof(struct diffstat_t));
586 for (i = 0; i < q->nr; i++) {
587 struct diff_filepair *p = q->queue[i];
588 //if (check_pair_status(p))
589 diff_flush_stat(p, &p_Rev->diffopt, &p_Rev->diffstat);
592 free_all_pack();
593 if(file)
594 *file = q;
595 if(count)
596 *count = q->nr;
597 return 0;
600 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)
602 struct diff_queue_struct *q = &diff_queued_diff;
603 struct rev_info *p_Rev;
604 p_Rev = (struct rev_info *)diff;
606 q = (struct diff_queue_struct *)file;
607 if(file == 0)
608 return -1;
609 if(i>=q->nr)
610 return -1;
612 assert(newname && oldname && status);
614 *newname = q->queue[i]->two->path;
615 *oldname = q->queue[i]->one->path;
616 *status = q->queue[i]->status;
618 if(p_Rev->diffstat.files)
620 int j;
621 for(j=0;j<p_Rev->diffstat.nr;j++)
623 if(strcmp(*newname,p_Rev->diffstat.files[j]->name)==0)
624 break;
626 if( j== p_Rev->diffstat.nr)
628 *IsBin=1;
629 *inc=0;
630 *dec=0;
631 return 0;
633 if(IsBin)
634 *IsBin = p_Rev->diffstat.files[j]->is_binary;
635 if(inc)
636 *inc = (int)p_Rev->diffstat.files[j]->added;
637 if(dec)
638 *dec = (int)p_Rev->diffstat.files[j]->deleted;
639 }else
641 *IsBin=1;
642 *inc=0;
643 *dec=0;
646 return 0;
649 int git_read_tree(GIT_HASH hash,read_tree_fn_t fn, void *context)
651 struct tree * root;
652 int ret;
653 reprepare_packed_git();
654 root = parse_tree_indirect(hash);
656 if (!root)
658 free_all_pack();
659 return -1;
661 ret = read_tree_recursive(root, NULL, 0, 0, NULL, fn, context);
662 free_all_pack();
663 return ret;
666 int git_add_exclude(const char *string, const char *base,
667 int baselen, struct exclude_list *which, int lineno)
669 add_exclude(string, base, baselen, which, lineno);
670 return 0;
673 int git_create_exclude_list(EXCLUDE_LIST *which)
675 *which = malloc(sizeof(struct exclude_list));
676 memset(*which,0,sizeof(struct exclude_list));
677 return 0;
680 int git_free_exclude_list(EXCLUDE_LIST which)
682 int i=0;
683 struct exclude_list *p = (struct exclude_list *) which;
685 for(i=0; i<p->nr;i++)
687 free(p->excludes[i]);
689 free(p->excludes);
690 free(p);
691 return 0;
694 int git_check_excluded_1(const char *pathname,
695 int pathlen, const char *basename, int *dtype,
696 EXCLUDE_LIST el)
698 return is_excluded_from_list(pathname, pathlen, basename, dtype, el);
701 int git_get_notes(GIT_HASH hash, char **p_note)
703 struct strbuf sb;
704 size_t size;
705 strbuf_init(&sb,0);
706 format_display_notes(hash, &sb, "utf-8", 1);
707 *p_note = strbuf_detach(&sb,&size);
709 return 0;
712 struct cmd_struct {
713 const char *cmd;
714 int (*fn)(int, const char **, const char *);
715 int option;
718 #define RUN_SETUP (1<<0)
719 #define USE_PAGER (1<<1)
721 * require working tree to be present -- anything uses this needs
722 * RUN_SETUP for reading from the configuration file.
724 #define NEED_WORK_TREE (1<<2)
726 const char git_usage_string[] =
727 "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
728 " [-p|--paginate|--no-pager] [--no-replace-objects]\n"
729 " [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
730 " [-c name=value] [--help]\n"
731 " COMMAND [ARGS]";
733 const char git_more_info_string[] =
734 "See 'git help COMMAND' for more information on a specific command.";
736 static struct cmd_struct commands[] = {
737 { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
738 { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
739 { "annotate", cmd_annotate, RUN_SETUP },
740 { "apply", cmd_apply },
741 { "archive", cmd_archive },
742 { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
743 { "blame", cmd_blame, RUN_SETUP },
744 { "branch", cmd_branch, RUN_SETUP },
745 { "bundle", cmd_bundle },
746 { "cat-file", cmd_cat_file, RUN_SETUP },
747 { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
748 { "checkout-index", cmd_checkout_index,
749 RUN_SETUP | NEED_WORK_TREE},
750 { "check-ref-format", cmd_check_ref_format },
751 { "check-attr", cmd_check_attr, RUN_SETUP },
752 { "cherry", cmd_cherry, RUN_SETUP },
753 { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
754 { "clone", cmd_clone },
755 { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
756 { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
757 { "commit-tree", cmd_commit_tree, RUN_SETUP },
758 { "config", cmd_config },
759 { "count-objects", cmd_count_objects, RUN_SETUP },
760 { "describe", cmd_describe, RUN_SETUP },
761 { "diff", cmd_diff },
762 { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
763 { "diff-index", cmd_diff_index, RUN_SETUP },
764 { "diff-tree", cmd_diff_tree, RUN_SETUP },
765 { "fast-export", cmd_fast_export, RUN_SETUP },
766 { "fetch", cmd_fetch, RUN_SETUP },
767 { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
768 { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
769 { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
770 { "format-patch", cmd_format_patch, RUN_SETUP },
771 { "fsck", cmd_fsck, RUN_SETUP },
772 { "fsck-objects", cmd_fsck, RUN_SETUP },
773 { "gc", cmd_gc, RUN_SETUP },
774 { "get-tar-commit-id", cmd_get_tar_commit_id },
775 { "grep", cmd_grep },
776 { "hash-object", cmd_hash_object },
777 { "help", cmd_help },
778 { "index-pack", cmd_index_pack },
779 { "init", cmd_init_db },
780 { "init-db", cmd_init_db },
781 { "log", cmd_log, RUN_SETUP },
782 { "ls-files", cmd_ls_files, RUN_SETUP },
783 { "ls-tree", cmd_ls_tree, RUN_SETUP },
784 { "ls-remote", cmd_ls_remote },
785 { "mailinfo", cmd_mailinfo },
786 { "mailsplit", cmd_mailsplit },
787 { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
788 { "merge-base", cmd_merge_base, RUN_SETUP },
789 { "merge-file", cmd_merge_file },
790 { "merge-index", cmd_merge_index, RUN_SETUP },
791 { "merge-ours", cmd_merge_ours, RUN_SETUP },
792 { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
793 { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
794 { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
795 { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
796 { "merge-tree", cmd_merge_tree, RUN_SETUP },
797 { "mktag", cmd_mktag, RUN_SETUP },
798 { "mktree", cmd_mktree, RUN_SETUP },
799 { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
800 { "name-rev", cmd_name_rev, RUN_SETUP },
801 { "notes", cmd_notes, RUN_SETUP },
802 { "pack-objects", cmd_pack_objects, RUN_SETUP },
803 { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
804 { "patch-id", cmd_patch_id },
805 { "pickaxe", cmd_blame, RUN_SETUP },
806 { "prune", cmd_prune, RUN_SETUP },
807 { "prune-packed", cmd_prune_packed, RUN_SETUP },
808 { "push", cmd_push, RUN_SETUP },
809 { "read-tree", cmd_read_tree, RUN_SETUP },
810 { "receive-pack", cmd_receive_pack },
811 { "reflog", cmd_reflog, RUN_SETUP },
812 { "remote", cmd_remote, RUN_SETUP },
813 { "replace", cmd_replace, RUN_SETUP },
814 { "rerere", cmd_rerere, RUN_SETUP },
815 { "reset", cmd_reset, RUN_SETUP },
816 { "rev-list", cmd_rev_list, RUN_SETUP },
817 { "rev-parse", cmd_rev_parse },
818 { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
819 { "rm", cmd_rm, RUN_SETUP },
820 { "send-pack", cmd_send_pack, RUN_SETUP },
821 { "shortlog", cmd_shortlog, USE_PAGER },
822 { "show-branch", cmd_show_branch, RUN_SETUP },
823 { "show", cmd_show, RUN_SETUP },
824 { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
825 { "stripspace", cmd_stripspace },
826 { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
827 { "tag", cmd_tag, RUN_SETUP },
828 { "unpack-file", cmd_unpack_file, RUN_SETUP },
829 { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
830 { "update-index", cmd_update_index, RUN_SETUP },
831 { "update-ref", cmd_update_ref, RUN_SETUP },
832 { "update-server-info", cmd_update_server_info, RUN_SETUP },
833 { "upload-archive", cmd_upload_archive },
834 { "var", cmd_var },
835 { "verify-tag", cmd_verify_tag, RUN_SETUP },
836 { "version", cmd_version },
837 { "whatchanged", cmd_whatchanged, RUN_SETUP },
838 { "write-tree", cmd_write_tree, RUN_SETUP },
839 { "verify-pack", cmd_verify_pack },
840 { "show-ref", cmd_show_ref, RUN_SETUP },
841 { "pack-refs", cmd_pack_refs, RUN_SETUP },
844 int is_builtin(const char *s)
846 int i;
847 for (i = 0; i < ARRAY_SIZE(commands); i++) {
848 struct cmd_struct *p = commands+i;
849 if (!strcmp(s, p->cmd))
850 return 1;
852 return 0;
855 int git_run_cmd(char *cmd, char *arg)
858 int i=0;
859 char ** argv=0;
860 int argc=0;
862 git_init();
864 for(i=0;i< sizeof(commands) / sizeof(struct cmd_struct);i++)
866 if(strcmp(cmd,commands[i].cmd)==0)
868 int ret;
869 if(arg != NULL)
870 argv = strtoargv(arg,&argc);
872 ret = commands[i].fn(argc, argv, NULL);
874 if(argv)
875 free(argv);
877 discard_cache();
878 free_all_pack();
880 return ret;
885 return -1;
888 int git_for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
890 return for_each_reflog_ent(ref,fn,cb_data);
893 static int update_some(const unsigned char* sha1, struct strbuf* base,
894 const char *pathname, unsigned mode, int stage, void *context)
896 struct cache_entry *ce;
897 UNREFERENCED_PARAMETER(stage);
899 ce = (struct cache_entry *)context;
901 if (S_ISDIR(mode))
902 return READ_TREE_RECURSIVE;
904 hashcpy(ce->sha1, sha1);
905 memcpy(ce->name, base->buf, base->len);
906 memcpy(ce->name + base->len, pathname, strlen(pathname));
907 ce->ce_flags = create_ce_flags((unsigned int)(strlen(pathname) + base->len));
908 ce->ce_mode = create_ce_mode(mode);
910 return 0;
913 int git_checkout_file(const char* ref, const char* path, char* outputpath)
915 struct cache_entry *ce;
916 int ret;
917 GIT_HASH sha1;
918 struct tree * root;
919 struct checkout state;
920 struct pathspec pathspec;
921 const char *matchbuf[1];
922 ret = get_sha1(ref, sha1);
923 if(ret)
924 return ret;
926 reprepare_packed_git();
927 root = parse_tree_indirect(sha1);
929 if(!root)
931 free_all_pack();
932 return -1;
935 ce = xcalloc(1, cache_entry_size(strlen(path)));
937 matchbuf[0] = NULL;
938 parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, PATHSPEC_PREFER_CWD, path, matchbuf);
939 pathspec.items[0].nowildcard_len = pathspec.items[0].len;
940 ret = read_tree_recursive(root, "", 0, 0, &pathspec, update_some, ce);
941 free_pathspec(&pathspec);
943 if(ret)
945 free_all_pack();
946 free(ce);
947 return ret;
949 memset(&state, 0, sizeof(state));
950 state.force = 1;
951 state.refresh_cache = 0;
953 ret = write_entry(ce, outputpath, &state, 0);
954 free_all_pack();
955 free(ce);
956 return ret;
958 struct config_buf
960 char *buf;
961 const char *key;
962 size_t size;
963 int seen;
966 static int get_config(const char *key_, const char *value_, void *cb)
968 struct config_buf *buf;
969 buf=(struct config_buf*)cb;
970 if(strcmp(key_, buf->key))
971 return 0;
973 if (value_)
974 strncpy(buf->buf,value_,buf->size);
975 else
977 buf->buf[0] = 't';
978 buf->buf[1] = 'r';
979 buf->buf[2] = 'u';
980 buf->buf[3] = 'e';
981 buf->buf[4] = 0;
983 buf->seen = 1;
984 return 0;
988 // wchar_t wrapper for git_etc_gitconfig()
989 const wchar_t *wget_msysgit_etc(void)
991 static const wchar_t *etc_gitconfig = NULL;
992 wchar_t wpointer[MAX_PATH];
994 if (etc_gitconfig)
995 return etc_gitconfig;
997 if (xutftowcs_path(wpointer, git_etc_gitconfig()) < 0)
998 return NULL;
1000 etc_gitconfig = _wcsdup(wpointer);
1002 return etc_gitconfig;
1005 int git_get_config(const char *key, char *buffer, int size)
1007 char *local, *global, *globalxdg;
1008 const char *home, *system;
1009 struct config_buf buf;
1010 struct git_config_source config_source = { 0 };
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)
1035 config_source.file = local;
1036 git_config_with_options(get_config, &buf, &config_source, 1);
1038 if (!buf.seen && global)
1040 config_source.file = global;
1041 git_config_with_options(get_config, &buf, &config_source, 1);
1043 if (!buf.seen && globalxdg)
1045 config_source.file = globalxdg;
1046 git_config_with_options(get_config, &buf, &config_source, 1);
1048 if (!buf.seen && system)
1050 config_source.file = system;
1051 git_config_with_options(get_config, &buf, &config_source, 1);
1054 if(local)
1055 free(local);
1056 if(global)
1057 free(global);
1058 if (globalxdg)
1059 free(globalxdg);
1061 return !buf.seen;
1064 // taken from msysgit: compat/mingw.c
1065 const char *get_windows_home_directory(void)
1067 static const char *home_directory = NULL;
1068 struct strbuf buf = STRBUF_INIT;
1070 if (home_directory)
1071 return home_directory;
1073 home_directory = getenv("HOME");
1074 if (home_directory && *home_directory)
1075 return home_directory;
1077 strbuf_addf(&buf, "%s%s", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
1078 home_directory = strbuf_detach(&buf, NULL);
1080 return home_directory;
1083 // wchar_t wrapper for get_windows_home_directory()
1084 const wchar_t *wget_windows_home_directory(void)
1086 static const wchar_t *home_directory = NULL;
1087 wchar_t wpointer[MAX_PATH];
1089 if (home_directory)
1090 return home_directory;
1092 if (xutftowcs_path(wpointer, get_windows_home_directory()) < 0)
1093 return NULL;
1095 home_directory = _wcsdup(wpointer);
1097 return home_directory;
1100 int get_set_config(const char *key, const char *value, CONFIG_TYPE type)
1102 char * config_exclusive_filename = NULL;
1104 switch(type)
1106 case CONFIG_LOCAL:
1107 config_exclusive_filename = git_pathdup("config");
1108 break;
1109 case CONFIG_GLOBAL:
1110 case CONFIG_XDGGLOBAL:
1112 const char *home = get_windows_home_directory();
1113 if (home)
1115 if (type == CONFIG_GLOBAL)
1116 config_exclusive_filename = xstrdup(mkpath("%s/.gitconfig", home));
1117 else
1118 config_exclusive_filename = xstrdup(mkpath("%s/.config/git/config", home));
1121 break;
1124 if(!config_exclusive_filename)
1125 return -1;
1127 return git_config_set_multivar_in_file(config_exclusive_filename, key, value, NULL, 0);
1130 struct mailmap_info {
1131 char *name;
1132 char *email;
1135 struct mailmap_entry {
1136 /* name and email for the simple mail-only case */
1137 char *name;
1138 char *email;
1140 /* name and email for the complex mail and name matching case */
1141 struct string_list namemap;
1144 int git_read_mailmap(GIT_MAILMAP *mailmap)
1146 struct string_list *map;
1147 int result;
1149 if (!mailmap)
1150 return -1;
1152 *mailmap = NULL;
1153 if ((map = (struct string_list *)calloc(1, sizeof(struct string_list))) == NULL)
1154 return -1;
1156 if ((result = read_mailmap(map, NULL)) != 0)
1157 return result;
1159 *mailmap = map;
1160 return 0;
1163 const char * git_get_mailmap_author(GIT_MAILMAP mailmap, const char *email2, void *payload, const char *(*author2_cb)(void *))
1165 struct string_list *map;
1166 int imax, imin = 0;
1168 if (!mailmap)
1169 return NULL;
1171 map = (struct string_list *)mailmap;
1172 imax = map->nr - 1;
1173 while (imax >= imin)
1175 int i = imin + ((imax - imin) / 2);
1176 struct string_list_item *si = (struct string_list_item *)&map->items[i];
1177 struct mailmap_entry *me = (struct mailmap_entry *)si->util;
1178 int comp = strcmp(si->string, email2);
1180 if (!comp)
1182 if (me->namemap.nr)
1184 const char *author2 = author2_cb(payload);
1185 unsigned int j;
1186 for (j = 0; j < me->namemap.nr; ++j)
1188 struct string_list_item *sj = (struct string_list_item *)&me->namemap.items[j];
1189 struct mailmap_info *mi = (struct mailmap_info *)sj->util;
1191 if (!strcmp(sj->string, author2))
1192 return mi->name;
1196 return me->name;
1198 else if (comp < 0)
1199 imin = i + 1;
1200 else
1201 imax = i - 1;
1204 return NULL;
1207 void git_free_mailmap(GIT_MAILMAP mailmap)
1209 if (!mailmap)
1210 return;
1212 clear_mailmap((struct string_list *)mailmap);
1213 free(mailmap);