rpm  5.4.15
rpmgit.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 
7 #include <rpmiotypes.h>
8 #include <rpmio.h> /* for *Pool methods */
9 #include <rpmlog.h>
10 #include <rpmmacro.h>
11 #include <rpmurl.h>
12 #include <ugid.h>
13 #include <poptIO.h>
14 
15 #define _RPMGIT_INTERNAL
16 #include <rpmgit.h>
17 
18 #include "debug.h"
19 
20 /*@unchecked@*/
22 /*@unchecked@*/
23 const char * _rpmgit_dir; /* XXX GIT_DIR */
24 /*@unchecked@*/
25 const char * _rpmgit_tree; /* XXX GIT_WORK_TREE */
26 
27 /*@unchecked@*/
28 static int _rpmgit_threads; /* XXX git_threads_{init,shutdown} counter */
29 
30 #define SPEW(_t, _rc, _git) \
31  { if ((_t) || _rpmgit_debug ) \
32  fprintf(stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, (_git), \
33  (_rc)); \
34  }
35 
36 /*==============================================================*/
37 #if defined(WITH_LIBGIT2)
38 /*@-redef@*/
39 typedef struct key_s {
40  uint32_t v;
41 /*@observer@*/
42  const char *n;
43 } KEY;
44 /*@=redef@*/
45 
46 /*@observer@*/
47 static const char * tblName(uint32_t v, KEY * tbl, size_t ntbl)
48  /*@*/
49 {
50  const char * n = NULL;
51  static char buf[32];
52  size_t i;
53 
54  for (i = 0; i < ntbl; i++) {
55  if (v != tbl[i].v)
56  continue;
57  n = tbl[i].n;
58  break;
59  }
60  if (n == NULL) {
61  (void) snprintf(buf, sizeof(buf), "0x%x", (unsigned)v);
62  n = buf;
63  }
64  return n;
65 }
66 
67 static const char * fmtBits(uint32_t flags, KEY tbl[], size_t ntbl, char *t)
68  /*@modifies t @*/
69 {
70  char pre = '<';
71  char * te = t;
72  int i;
73 
74  sprintf(t, "0x%x", (unsigned)flags);
75  te = t;
76  te += strlen(te);
77  for (i = 0; i < 32; i++) {
78  uint32_t mask = (1 << i);
79  const char * name;
80 
81  if (!(flags & mask))
82  continue;
83 
84  name = tblName(mask, tbl, ntbl);
85  *te++ = pre;
86  pre = ',';
87  te = stpcpy(te, name);
88  }
89  if (pre == ',') *te++ = '>';
90  *te = '\0';
91  return t;
92 }
93 
94 #define _ENTRY(_v) { GIT_IDXENTRY_##_v, #_v, }
95 /*@unchecked@*/ /*@observer@*/
96 static KEY IDXEflags[] = {
97  _ENTRY(UPDATE),
98  _ENTRY(REMOVE),
99  _ENTRY(UPTODATE),
100  _ENTRY(ADDED),
101  _ENTRY(HASHED),
102  _ENTRY(UNHASHED),
103  _ENTRY(WT_REMOVE),
104  _ENTRY(CONFLICTED),
105  _ENTRY(UNPACKED),
106  _ENTRY(NEW_SKIP_WORKTREE),
107 
108  _ENTRY(INTENT_TO_ADD),
109  _ENTRY(SKIP_WORKTREE),
110  _ENTRY(EXTENDED2),
111 };
112 #undef _ENTRY
113 /*@unchecked@*/
114 static size_t nIDXEflags = sizeof(IDXEflags) / sizeof(IDXEflags[0]);
115 /*@observer@*/
116 static const char * fmtIDXEflags(uint32_t flags)
117  /*@*/
118 {
119  static char buf[BUFSIZ];
120  char * te = buf;
121  te = stpcpy(te, "\n\tflags: ");
122  (void) fmtBits(flags, IDXEflags, nIDXEflags, te);
123  return buf;
124 }
125 #define _IDXFLAGS(_idxeflags) fmtIDXEflags(_idxeflags)
126 
127 #define _ENTRY(_v) { GIT_REF_##_v, #_v, }
128 /*@unchecked@*/ /*@observer@*/
129 static KEY REFflags[] = {
130  _ENTRY(OID),
131  _ENTRY(SYMBOLIC),
132 };
133 #undef _ENTRY
134 /*@unchecked@*/
135 static size_t nREFflags = sizeof(REFflags) / sizeof(REFflags[0]);
136 /*@observer@*/
137 static const char * fmtREFflags(uint32_t flags)
138  /*@*/
139 {
140  static char buf[BUFSIZ];
141  char * te = buf;
142  te = stpcpy(te, "\n\tflags: ");
143  (void) fmtBits(flags, REFflags, nREFflags, te);
144  return buf;
145 }
146 #define _REFFLAGS(_refflags) fmtREFflags(_refflags)
147 
148 /*==============================================================*/
149 
150 static void check(int error, const char *message, const char *extra)
151 {
152  const git_error *lg2err;
153  const char *lg2msg = "";
154  const char *lg2spacer = "";
155 
156  if (!error)
157  return;
158 
159  if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
160  lg2msg = lg2err->message;
161  lg2spacer = " - ";
162  }
163 
164  if (extra)
165  fprintf(stderr, "%s '%s' [%d]%s%s\n",
166  message, extra, error, lg2spacer, lg2msg);
167  else
168  fprintf(stderr, "%s [%d]%s%s\n",
169  message, error, lg2spacer, lg2msg);
170 
171  exit(1);
172 }
173 
174 static int Xchkgit(/*@unused@*/ rpmgit git, const char * msg,
175  int error, int printit,
176  const char * func, const char * fn, unsigned ln)
177  /*@*/
178 {
179  int rc = error;
180 
181  if (printit && rc) {
182  const git_error * e = giterr_last();
183  char * message = (e ? e->message : "");
184  int klass = (e ? e->klass : -12345);
185  rpmlog(RPMLOG_ERR, "%s:%s:%u: %s(%d): %s(%d)\n",
186  func, fn, ln, msg, rc, message, klass);
187  }
188 
189  return rc;
190 }
191 #define chkgit(_git, _msg, _error) \
192  Xchkgit(_git, _msg, _error, _rpmgit_debug, __FUNCTION__, __FILE__, __LINE__)
193 
194 void rpmgitPrintOid(const char * msg, const void * _oidp, void * _fp)
195 {
196  FILE * fp = (_fp ? _fp : stderr);
197  const git_oid * oidp = _oidp;
198  char * t;
199  if (oidp) {
200  t = git_oid_allocfmt(oidp);
201  git_oid_fmt(t, oidp);
202  } else
203  t = strdup("NULL");
204 if (msg) fprintf(fp, "%s:", msg);
205 fprintf(fp, " %s\n", t);
206  t = _free(t);
207 }
208 
209 void rpmgitPrintTime(const char * msg, time_t _Ctime, void * _fp)
210 {
211  FILE * fp = (_fp ? _fp : stderr);
212  static const char _fmt[] = "%c";
213  struct tm tm;
214  char _b[BUFSIZ];
215  size_t _nb = sizeof(_b);
216  size_t nw = strftime(_b, _nb-1, _fmt, localtime_r(&_Ctime, &tm));
217  (void)nw;
218 if (msg) fprintf(fp, "%s:", msg);
219 fprintf(fp, " %s", _b);
220 }
221 
222 void rpmgitPrintSig(const char * msg, const void * ___S, void * _fp)
223 {
224  FILE * fp = (_fp ? _fp : stderr);
225  const git_signature * S = ___S;
226 assert(S != NULL);
227 if (msg) fprintf(fp, "%s:", msg);
228 fprintf(fp, " %s <%s>", S->name, S->email);
229 #ifdef DYING
230 rpmgitPrintTime(NULL, (time_t)S->when.time, fp);
231 #else
232 fprintf(fp, " %lu", (unsigned long)S->when.time);
233 fprintf(fp, " %.02d%.02d", (S->when.offset/60), (S->when.offset%60));
234 #endif
235 fprintf(fp, "\n");
236 }
237 
238 void rpmgitPrintIndex(void * ___I, void * _fp)
239 {
240  FILE * fp = (FILE *) _fp;
241  git_index * I = (git_index *) ___I;
242  unsigned Icnt;
243  unsigned i;
244 
245 assert(I != NULL);
246 if (_rpmgit_debug < 0 && fp == NULL) fp = stderr;
247 if (fp == NULL) return;
248 
249  Icnt = git_index_entrycount(I);
250  fprintf(fp, "-------- Index(%u)\n", Icnt);
251  for (i = 0; i < Icnt; i++) {
252  const git_index_entry * E = git_index_get_byindex(I, i);
253 
254  fprintf(fp, "=== %s:", E->path);
255 
256  rpmgitPrintOid("\n\t oid", &E->id, fp);
257 
258  fprintf(fp, "\t dev: %x", (unsigned)E->dev);
259  fprintf(fp, "\n\t ino: %lu", (unsigned long)E->ino);
260  fprintf(fp, "\n\t mode: %o", (unsigned)E->mode);
261 
262  fprintf(fp, "\n\t user: %s", uidToUname((uid_t)E->uid));
263  fprintf(fp, "\n\tgroup: %s", gidToGname((gid_t)E->gid));
264 
265  fprintf(fp, "\n\t size: %lu", (unsigned long)E->file_size);
266 
267  rpmgitPrintTime("\n\tctime", E->ctime.seconds, fp);
268 
269  rpmgitPrintTime("\n\tmtime", E->mtime.seconds, fp);
270 
271  fprintf(fp, "%s", _IDXFLAGS(E->flags));
272 
273  fprintf(fp, "\n");
274  }
275 }
276 
277 #ifdef DYING
278 static const char * rpmgitOtype(git_otype otype)
279  /*@*/
280 {
281  const char * s = "unknown";
282  switch(otype) {
283  case GIT_OBJ_ANY: s = "any"; break;
284  case GIT_OBJ_BAD: s = "bad"; break;
285  case GIT_OBJ__EXT1: s = "EXT1"; break;
286  case GIT_OBJ_COMMIT: s = "commit"; break;
287  case GIT_OBJ_TREE: s = "tree"; break;
288  case GIT_OBJ_BLOB: s = "blob"; break;
289  case GIT_OBJ_TAG: s = "tag"; break;
290  case GIT_OBJ__EXT2: s = "EXT2"; break;
291  case GIT_OBJ_OFS_DELTA: s = "delta off"; break;
292  case GIT_OBJ_REF_DELTA: s = "delta oid"; break;
293  }
294  return s;
295 }
296 #endif
297 
298 void rpmgitPrintTree(void * ___T, void * _fp)
299 {
300  git_tree * T = (git_tree *) ___T;
301  FILE * fp = (FILE *) _fp;
302  unsigned Tcnt;
303  unsigned i;
304 
305 assert(T != NULL);
306 if (_rpmgit_debug < 0 && fp == NULL) fp = stderr;
307 if (fp == NULL) return;
308 
309 #ifdef DYING
310  rpmgitPrintOid("-------- Toid", git_tree_id(T), fp);
311 #endif
312  Tcnt = git_tree_entrycount(T);
313 #ifdef DYING
314 fprintf(fp, " Tcnt: %u\n", Tcnt);
315 #endif
316  for (i = 0; i < Tcnt; i++) {
317  const git_tree_entry * E = git_tree_entry_byindex(T, i);
318  char * t;
319 assert(E != NULL);
320 #ifdef DYING
321 fprintf(fp, " Eattrs: 0%o\n", git_tree_entry_filemode(E));
322 fprintf(fp, " Ename: %s\n", git_tree_entry_name(E));
323  rpmgitPrintOid(" Eoid", git_tree_entry_id(E), fp);
324 fprintf(fp, " Etype: %s\n", rpmgitOtype(git_tree_entry_type(E)));
325 #else
326  t = git_oid_allocfmt(git_tree_entry_id(E));
327  fprintf(fp, "%06o %.4s %s\t%s\n",
328  git_tree_entry_filemode(E),
329  git_object_type2string(git_tree_entry_type(E)),
330  t,
331  git_tree_entry_name(E));
332  t = _free(t);
333 #endif
334  }
335 }
336 
337 void rpmgitPrintCommit(rpmgit git, void * ___C, void * _fp)
338 {
339  FILE * fp = (FILE *) _fp;
340  git_commit * C = ___C;
341  unsigned Pcnt;
342  unsigned i;
343 
344 assert(C != NULL);
345 if (_rpmgit_debug < 0 && fp == NULL) fp = stderr;
346 if (fp == NULL) return;
347 
348 #ifdef DYING
349  rpmgitPrintOid("-------- Coid", git_commit_id(C), fp);
350 fprintf(fp, " Cmsgenc: %s\n", git_commit_message_encoding(C));
351 fprintf(fp, " Cmsg: %s\n", git_commit_message(C));
352 
353 rpmgitPrintTime(" Ctime", git_commit_time(C), fp);
354 fprintf(fp, "\n");
355 
356 fprintf(fp, " Ctz: %d\n", git_commit_time_offset(C));
357  rpmgitPrintSig(" Cauthor", git_commit_author(C), fp);
358  rpmgitPrintSig(" Ccmtter", git_commit_committer(C), fp);
359  rpmgitPrintOid(" Toid", git_commit_tree_oid(C), fp);
360 
361  Pcnt = git_commit_parentcount(C);
362 fprintf(fp, " Pcnt: %u\n", Pcnt);
363  for (i = 0; i < Pcnt; i++) {
364  git_commit * E;
365  xx = chkgit(git, "git_commit_parent",
366  git_commit_parent(&E, C, i));
367  const git_oid * Poidp = git_commit_parent_oid(E, i);
368  rpmgitPrintOid(" Poid", Poidp, fp);
369  }
370 #else
371  rpmgitPrintOid("tree", git_commit_tree_id(C), fp);
372  Pcnt = git_commit_parentcount(C);
373  for (i = 0; i < Pcnt; i++)
374  rpmgitPrintOid("parent", git_commit_parent_id(C, i), fp);
375  rpmgitPrintSig("author", git_commit_author(C), fp);
376  rpmgitPrintSig("committer", git_commit_committer(C), fp);
377 fprintf(fp, "encoding: %s\n", git_commit_message_encoding(C));
378 fprintf(fp, "\n%s", git_commit_message(C));
379 #endif
380 }
381 
382 void rpmgitPrintTag(rpmgit git, void * _tag, void * _fp)
383 {
384  FILE * fp = (_fp ? _fp : stderr);
385  git_tag * tag = (git_tag *) _tag;
386 
387 assert(tag != NULL);
388 #ifdef NOTYET
389 if (_rpmgit_debug >= 0) return;
390 #endif
391 
392 #ifdef DYING
393  rpmgitPrintOid("-------- TAG", git_tag_id(tag), fp);
394 fprintf(fp, " name: %s\n", git_tag_name(tag));
395 fprintf(fp, " type: %s\n", git_object_type2string(git_tag_type(tag)));
396  rpmgitPrintOid(" target", git_tag_target_id(tag), fp);
397  rpmgitPrintSig(" tagger", git_tag_tagger(tag), fp);
398 fprintf(fp, "\n%s", git_tag_message(tag));
399 #else
400  rpmgitPrintOid("object", git_tag_target_id(tag), fp);
401 fprintf(fp, "type: %s\n", git_object_type2string(git_tag_target_type(tag)));
402 fprintf(fp, "tag: %s\n", git_tag_name(tag));
403 /* XXX needs strftime(3) */
404  rpmgitPrintSig("tagger", git_tag_tagger(tag), fp);
405 fprintf(fp, "\n%s", git_tag_message(tag));
406 #endif
407 
408 }
409 
410 void rpmgitPrintHead(rpmgit git, void * ___H, void * _fp)
411 {
412  FILE * fp = (_fp ? _fp : stderr);
413  git_reference * H = (___H ? ___H : git->H);
414  git_reference * Hresolved = NULL;
415  int xx;
416 
417  if (H == NULL) {
418  xx = chkgit(git, "git_repository_head",
419  git_repository_head((git_reference **)&git->H, git->R));
420  H = git->H;
421  }
422 assert(H != NULL);
423 if (_rpmgit_debug >= 0) return;
424 
425  xx = chkgit(git, "git_reference_resolve",
426  git_reference_resolve(&Hresolved, H));
427 
428  rpmgitPrintOid("------- Hpeel", git_reference_target_peel(H), fp);
429  rpmgitPrintOid(" Htarget", git_reference_target(H), fp);
430 fprintf(fp, " Hname: %s\n", git_reference_name(H));
431 fprintf(fp, " Hresolved: %p\n", Hresolved);
432 fprintf(fp, " Howner: %p", git_reference_owner(H));
433 #ifdef DYING
434 fprintf(fp, " Hrtype: %d\n", (int)git_reference_type(H));
435 #else
436 fprintf(fp, "%s\n", _REFFLAGS(git_reference_type(H)));
437 #endif
438 
439  if (Hresolved) /* XXX leak */
440  git_reference_free(Hresolved);
441 
442 }
443 
444 void rpmgitPrintRepo(rpmgit git, void * ___R, void * _fp)
445 {
446  FILE * fp = (_fp ? _fp : stderr);
447  git_repository * R = ___R;
448  const char * fn;
449 
450 if (_rpmgit_debug >= 0) return;
451 
452 fprintf(fp, "head_detached: %d\n", git_repository_head_detached(R));
453 fprintf(fp, " head_unborn: %d\n", git_repository_head_unborn(R));
454 fprintf(fp, " is_empty: %d\n", git_repository_is_empty(R));
455 fprintf(fp, " is_bare: %d\n", git_repository_is_bare(R));
456  fn = git_repository_path(R);
457 fprintf(fp, " path: %s\n", fn);
458  fn = git_repository_workdir(R);
459 fprintf(fp, " workdir: %s\n", fn);
460  /* XXX get_repository_config */
461  /* XXX get_repository_odb */
462  /* XXX get_repository_refdb */
463  /* XXX get_repository_index */
464  /* XXX get_repository_message */
465 fprintf(fp, " state: %d\n", git_repository_state(R));
466 fprintf(fp, " namespace: %s\n", git_repository_get_namespace(R));
467 fprintf(fp, " is_shallow: %d\n", git_repository_is_empty(R));
468 
469 }
470 #endif /* defined(WITH_LIBGT2) */
471 
472 /*==============================================================*/
473 int rpmgitInit(rpmgit git, void * initopts)
474 {
475  int rc = -1;
476 #if defined(WITH_LIBGIT2)
477  git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
478 
479  if (git->R) { /* XXX leak */
480  git_repository_free(git->R);
481  git->R = NULL;
482  }
483 
484  if (initopts == NULL) {
485  opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't use default */
486  if (git->is_bare)
487  opts.flags |= GIT_REPOSITORY_INIT_BARE;
488  initopts = (void *) &opts;
489  }
490  /* XXX git_repository_init_ext(&git->R, git->fn, &opts); */
491  /* XXX git->repodir? */
492  rc = chkgit(git, "git_repository_init_ext",
493  git_repository_init_ext((git_repository **)&git->R, git->fn,
494  (git_repository_init_options *)initopts));
495  if (rc)
496  goto exit;
497 if (_rpmgit_debug < 0) rpmgitPrintRepo(git, git->R, git->fp);
498 
499 exit:
500 #endif /* defined(WITH_LIBGT2) */
501 SPEW(0, rc, git);
502  return rc;
503 }
504 
505 int rpmgitAddFile(rpmgit git, const char * fn)
506 {
507  int rc = -1;
508 #if defined(WITH_LIBGIT2)
509 
510  /* XXX Strip out workdir prefix if present. */
511  { const char * s = git_repository_workdir(git->R);
512  size_t ns = strlen(s);
513  if (strlen(fn) > ns && !strncmp(fn, s, ns))
514  fn += ns;
515  }
516 
517  /* Upsert the file into the index. */
518  rc = chkgit(git, "git_index_add_bypath",
519  git_index_add_bypath(git->I, fn));
520  if (rc)
521  goto exit;
522 
523  /* Write the index to disk. */
524  rc = chkgit(git, "git_index_write",
525  git_index_write(git->I));
526  if (rc)
527  goto exit;
528 
529 exit:
530 #endif /* defined(WITH_LIBGT2) */
531 SPEW(0, rc, git);
532  return rc;
533 }
534 
535 int rpmgitCommit(rpmgit git, const char * msg)
536 {
537  int rc = -1;
538 #if defined(WITH_LIBGIT2)
539  static const char _msg[] = "WDJ commit";
540  static const char * update_ref = "HEAD";
541  static const char * message_encoding = "UTF-8";
542  git_commit * old_head = NULL;
543 
544  const char * message = (msg ? msg : _msg);
545  const char * user_name =
546  (git->user_name ? git->user_name : "Jeff Johnson");
547  const char * user_email =
548  (git->user_email ? git->user_email : "jbj@jbj.org");
549 
550  git_oid _oidC;
551  git_oid * Coidp = &_oidC;
552  git_signature * Cauthor = NULL;
553  git_signature * Ccmtter = NULL;
554 
555  git_oid _oidT;
556 
557  /* Find the root tree oid. */
558  rc = chkgit(git, "git_index_write_tree",
559  git_index_write_tree(&_oidT, git->I));
560  if (rc)
561  goto exit;
562 if (_rpmgit_debug < 0) rpmgitPrintOid(" oidT", &_oidT, NULL);
563 
564  /* Find the tree object. */
565  rc = chkgit(git, "git_tree_lookup",
566  git_tree_lookup((git_tree **)&git->T, git->R, &_oidT));
567  if (rc)
568  goto exit;
569 
570 if (_rpmgit_debug < 0) rpmgitPrintTree(git->T, NULL);
571 
572  /* Find the current HEAD. */
573  rc = chkgit(git, "git_revparse_single",
574  git_revparse_single((git_object**)&old_head,
575  git->R, update_ref));
576  if (rc)
577  goto exit;
578 
579  /* Commit the added file. */
580  rc = chkgit(git, "git_signature_now",
581  git_signature_now(&Cauthor, user_name, user_email));
582  if (rc)
583  goto exit;
584  rc = chkgit(git, "git_signature_now",
585  git_signature_now(&Ccmtter, user_name, user_email));
586  if (rc)
587  goto exit;
588 
589 
590  rc = chkgit(git, "git_commit_create",
591  git_commit_create_v(Coidp, git->R, update_ref,
592  Cauthor, Ccmtter,
593  message_encoding, message,
594  git->T,
595  1, old_head));
596  if (rc)
597  goto exit;
598 
599 if (_rpmgit_debug < 0) rpmgitPrintOid(" oidC", Coidp, NULL);
600 
601  /* Find the commit object. */
602  rc = chkgit(git, "git_commit_lookup",
603  git_commit_lookup((git_commit **)&git->C, git->R, Coidp));
604 
605  if (git->T) { /* XXX leak */
606  git_tree_free(git->T);
607  git->T = NULL;
608  }
609  rc = chkgit(git, "git_commit_tree",
610  git_commit_tree((git_tree **)&git->T, git->C));
611 
612 exit:
613  if (Cauthor)
614  git_signature_free((git_signature *)Cauthor);
615  if (Ccmtter)
616  git_signature_free((git_signature *)Ccmtter);
617 #endif /* defined(WITH_LIBGT2) */
618 SPEW(0, rc, git);
619 
620  return rc;
621 }
622 
623 #if defined(WITH_LIBGIT2)
624 static int rpmgitConfigCB(const git_config_entry * CE, void * _git)
625 {
626  rpmgit git = (rpmgit) _git;
627  const char * var_name = CE->name;
628  const char * value = CE->value;
629  int rc = 0;
630 
631  if (!strcmp("core.bare", var_name)) {
632  git->core_bare = strcmp(value, "false") ? 1 : 0;
633  } else
634  if (!strcmp("core.repositoryformatversion", var_name)) {
635  git->core_repositoryformatversion = atol(value);
636  } else
637  if (!strcmp("user.name", var_name)) {
638  git->user_name = _free(git->user_name);
639  git->user_name = xstrdup(value);
640  } else
641  if (!strcmp("user.email", var_name)) {
642  git->user_email = _free(git->user_email);
643  git->user_email = xstrdup(value);
644  }
645  if (git->fp)
646  fprintf(git->fp, "%s: %s\n", var_name, value);
647 
648 SPEW(0, rc, git);
649  return rc;
650 }
651 #endif /* defined(WITH_LIBGT2) */
652 
654 {
655  int rc = -1;
656 
657 #if defined(WITH_LIBGIT2)
658  /* Read/print/save configuration info. */
659  rc = chkgit(git, "git_repository_config",
660  git_repository_config((git_config **)&git->cfg, git->R));
661  if (rc)
662  goto exit;
663 
664  rc = chkgit(git, "git_config_foreach",
665  git_config_foreach(git->cfg, rpmgitConfigCB, git));
666  if (rc)
667  goto exit;
668 
669 exit:
670 #endif /* defined(WITH_LIBGT2) */
671 SPEW(0, rc, git);
672  return rc;
673 }
674 
675 const char * rpmgitOid(rpmgit git, const void * _oid)
676 {
677 #if defined(WITH_LIBGIT2)
678  git_oid * oid = (git_oid *) _oid;
679  git->str[0] = '\0';
680  git_oid_tostr(git->str, sizeof(git->str), oid);
681  git->str[RPMGIT_OID_HEXSZ] = '\0';
682 #else
683  git->str[0] = '\0';
684 #endif /* defined(WITH_LIBGT2) */
685  return git->str;
686 }
687 
689 {
690  int rc = 0;
691 
692 #if defined(WITH_LIBGIT2)
693  /* XXX other sanity checks and side effects? */
694  if (git->R) {
695  git_repository_free(git->R);
696  git->R = NULL;
697  git->repodir = _free(git->repodir);
698  }
699 #endif /* defined(WITH_LIBGT2) */
700 
701 SPEW(0, rc, git);
702  return rc;
703 }
704 
705 int rpmgitOpen(rpmgit git, const char * repodir)
706 {
707  int rc = 0;
708 
709 #if defined(WITH_LIBGIT2)
710  /* XXX lazy close? */
711  if (git->R == NULL) {
712  if (repodir) {
713  git->repodir = _free(git->repodir);
714  git->repodir = Realpath(repodir, NULL);
715  } else if (git->repodir == NULL) {
716  const char * dn = (git->fn ? git->fn : ".");
717  git->repodir = Realpath(dn, NULL);
718  }
719  rc = chkgit(git, "git_repository_open_ext",
720  git_repository_open_ext((git_repository **)&git->R, git->repodir, 0, NULL));
721  }
722 
723 #endif /* defined(WITH_LIBGT2) */
724 SPEW(0, rc, git);
725  return rc;
726 }
727 
728 /*==============================================================*/
729 
731 {
732  int rc = -1;
733 #if defined(WITH_LIBGIT2)
734  const git_tree_entry *entry;
735  git_object *objt;
736  (void)entry;
737  (void)objt;
738 
739 #endif
740 SPEW(0, rc, git);
741  return rc;
742 }
743 
745 {
746  int rc = -1;
747 #if defined(WITH_LIBGIT2)
748  FILE * fp = (git->fp ? git->fp : stdout);
749  git_revwalk * walk;
750  git_oid oid;
751  int xx;
752 
753  xx = chkgit(git, "git_revwalk_new",
754  git_revwalk_new(&walk, git->R));
755  git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE);
756  xx = chkgit(git, "git_revwalk_push_head",
757  git_revwalk_push_head(walk));
758  git->walk = (void *) walk;
759 
760  while ((xx = chkgit(git, "git_revwalk_next",
761  git_revwalk_next(&oid, walk))) == GIT_OK)
762  {
763  git_commit * C;
764  const git_signature * S;
765 
766  xx = chkgit(git, "git_commit_lookup",
767  git_commit_lookup(&C, git->R, &oid));
768 rpmgitPrintOid("Commit", git_commit_id(C), fp);
769  S = git_commit_author(C);
770 fprintf(fp, "Author: %s <%s>", S->name, S->email);
771 rpmgitPrintTime("\n Date", (time_t)S->when.time, fp);
772 fprintf(fp, "\n%s", git_commit_message(C));
773 fprintf(fp, "\n");
774  git_commit_free(C);
775  }
776 
777  git->walk = NULL;
778  git_revwalk_free(walk);
779  walk = NULL;
780  rc = 0; /* XXX */
781  goto exit; /* XXX GCC warning */
782 
783 exit:
784 #endif
785 SPEW(0, rc, git);
786  return rc;
787 }
788 
790 {
791  int rc = -1;
792 #if defined(WITH_LIBGIT2)
793  FILE * fp = (git->fp ? git->fp : stdout);
794  unsigned ecount;
795  unsigned i;
796  size_t nt = 128;
797  char * t = alloca(nt);
798  size_t nb;
799  int xx;
800 
801  xx = chkgit(git, "git_repository_index",
802  git_repository_index((git_index **)&git->I, git->R));
803  xx = chkgit(git, "git_index_read",
804  git_index_read(git->I, 0));
805 
806  ecount = git_index_entrycount(git->I);
807  for (i = 0; i < ecount; i++) {
808  static const char _fmt[] = "%c";
809  const git_index_entry * e = git_index_get_byindex(git->I, i);
810  git_oid oid = e->id;
811  time_t mtime;
812  struct tm tm;
813 
814  fprintf(fp, "=== %s:", e->path);
815 
816  git_oid_fmt(t, &oid);
817  t[GIT_OID_HEXSZ] = '\0';
818  fprintf(fp, "\n\t oid: %s", t);
819 
820  fprintf(fp, "\n\t dev: %x", (unsigned)e->dev);
821  fprintf(fp, "\n\t ino: %lu", (unsigned long)e->ino);
822  fprintf(fp, "\n\t mode: %o", (unsigned)e->mode);
823 
824  fprintf(fp, "\n\t user: %s", uidToUname((uid_t)e->uid));
825  fprintf(fp, "\n\tgroup: %s", gidToGname((gid_t)e->gid));
826 
827  fprintf(fp, "\n\t size: %lu", (unsigned long)e->file_size);
828 
829  mtime = e->ctime.seconds;
830  nb = strftime(t, nt, _fmt, localtime_r(&mtime, &tm));
831  fprintf(fp, "\n\tctime: %s", t);
832 
833  mtime = e->mtime.seconds;
834  nb = strftime(t, nt, _fmt, localtime_r(&mtime, &tm));
835  fprintf(fp, "\n\tmtime: %s", t);
836 
837  fprintf(fp, "%s", _IDXFLAGS(e->flags));
838 
839  fprintf(fp, "\n");
840  }
841 
842  git_index_free(git->I);
843  git->I = NULL;
844  rc = 0;
845 #endif
846 SPEW(0, rc, git);
847  return rc;
848 }
849 
851 {
852  int rc = -1;
853 #if defined(WITH_LIBGIT2) && defined(NOTYET)
854  FILE * fp = (git->fp ? git->fp : stdout);
855  git_odb * odb = git_repository_database(git->R);
856  git_odb_object * obj;
857  git_oid oid;
858  unsigned char * data;
859  const char * str_type;
860  git_otype otype;
861 
862  rc = git_odb_read(&obj, odb, &oid);
863  data = (char *)git_odb_object_data(obj);
864  otype = git_odb_object_type(oid);
865  str_type = git_object_type2string(otype);
866  fprintf(fp, "object length and type: %d, %s\n",
867  (int)git_odb_object_size(obj), str_type);
868 
869  git_odb_object_close(obj);
870 
871  rc = 0; /* XXX */
872 #endif
873 SPEW(0, rc, git);
874  return rc;
875 }
876 
878 {
879  int rc = -1;
880 #if defined(WITH_LIBGIT2) && defined(NOTYET)
881  FILE * fp = (git->fp ? git->fp : stdout);
882  git_odb * odb = git_repository_database(git->R);
883  git_oid oid;
884  size_t nb = GIT_OID_HEXSZ;
885  char * b = alloca(nb+1);
886 
887  nb = strncpy(b, nb, "blah blah") - b;
888  rc = git_odb_write(&oid, odb, b, nb, GIT_OBJ_BLOB);
889  git_oid_format(b, &oid);
890  fprintf(fp, "Written Object: %s\n", b);
891 
892  rc = 0; /* XXX */
893 #endif
894 SPEW(0, rc, git);
895  return rc;
896 }
897 
898 /*==============================================================*/
899 
900 static int rpmgitPopt(rpmgit git, int argc, char *argv[], poptOption opts)
901 {
902  static int _popt_flags = POPT_CONTEXT_POSIXMEHARDER;
903  int rc;
904 int xx;
905 
906 if (_rpmgit_debug) argvPrint("before", (ARGV_t)argv, NULL);
907 #if defined(WITH_LIBGIT2)
908 if (_rpmgit_debug && git->R) rpmgitPrintRepo(git, git->R, git->fp);
909 #endif /* defined(WITH_LIBGIT2) */
910 
911  git->con = poptFreeContext(git->con); /* XXX necessary? */
912  git->con = poptGetContext(argv[0], argc, (const char **)argv, opts, _popt_flags);
913 
914  while ((rc = poptGetNextOpt(git->con)) > 0) {
915  const char * arg = poptGetOptArg(git->con);
916  arg = _free(arg);
917  }
918  if (rc < -1) {
919  fprintf(stderr, "%s: %s: %s\n", argv[0],
920  poptBadOption(git->con, POPT_BADOPTION_NOALIAS),
921  poptStrerror(rc));
922  git->con = poptFreeContext(git->con);
923  }
924 
925  git->av = argvFree(git->av); /* XXX necessary? */
926  if (git->con)
927  xx = argvAppend(&git->av, (ARGV_t)poptGetArgs(git->con));
928  git->ac = argvCount(git->av);
929 if (_rpmgit_debug) argvPrint(" after", git->av, NULL);
930  return rc;
931 }
932 
933 /*==============================================================*/
934 /* parse the tail of the --shared= argument */
935 #if defined(WITH_LIBGIT2)
936 static uint32_t parse_shared(const char * shared)
937 {
938  if (!strcmp(shared, "false")
939  || !strcmp(shared, "umask"))
940  return GIT_REPOSITORY_INIT_SHARED_UMASK;
941  else if (!strcmp(shared, "true")
942  || !strcmp(shared, "group"))
943  return GIT_REPOSITORY_INIT_SHARED_GROUP;
944  else if (!strcmp(shared, "all")
945  || !strcmp(shared, "world")
946  || !strcmp(shared, "everybody"))
947  return GIT_REPOSITORY_INIT_SHARED_ALL;
948  else if (shared[0] == '0') {
949  char *end = NULL;
950  long val = strtol(shared + 1, &end, 8); /* XXX permit other bases */
951  if (end == shared + 1 || *end != 0)
952  goto exit; /* XXX error on trailing crap */
953  return (uint32_t)val;
954  }
955  /* XXX error on unknown keyword */
956 
957 exit:
958  return 0;
959 }
960 
961 /*
962  * Unlike regular "git init", this example shows how to create an initial
963  * empty commit in the repository. This is the helper function that does
964  * that.
965  */
966 static int create_initial_commit(rpmgit git, void * ___R)
967 {
968  FILE * fp = (git->fp ? git->fp : stderr);
969  git_oid Toid;
970  git_oid Coid;
971  int xx;
972 
973  /* First use the config to initialize a commit signature for the user */
974  xx = chkgit(git, "git_signature_default",
975  git_signature_default((git_signature **)&git->S, git->R));
976  if (xx < 0) {
977  fprintf(fp, "Unable to create a commit signature.\n"
978  "Perhaps 'user.name' and 'user.email' are not set\n");
979  goto exit;
980  }
981 
982  /* Now let's create an empty tree for this commit */
983  xx = chkgit(git, "git_repository_index",
984  git_repository_index((git_index **)&git->I, git->R));
985  if (xx < 0) {
986  fprintf(fp, "Could not open repository index\n");
987  goto exit;
988  }
989 
990  /* Outside of this example, you could call git_index_add_bypath()
991  * here to put actual files into the index. For our purposes, we'll
992  * leave it empty for now.
993  */
994  xx = chkgit(git, "git_index_write_tree",
995  git_index_write_tree(&Toid, git->I));
996  if (xx < 0) {
997  fprintf(fp, "Unable to write initial tree from index\n");
998  goto exit;
999  }
1000 
1001  git_index_free(git->I);
1002  git->I = NULL;
1003 
1004  xx = chkgit(git, "git_tree_lookup",
1005  git_tree_lookup((git_tree **)&git->T, git->R, &Toid));
1006  if (xx < 0) {
1007  fprintf(fp, "Could not look up initial tree\n");
1008  goto exit;
1009  }
1010 
1011  /* Ready to create the initial commit
1012  *
1013  * Normally creating a commit would involve looking up the current
1014  * HEAD commit and making that be the parent of the initial commit,
1015  * but here this is the first commit so there will be no parent.
1016  */
1017  xx = chkgit(git, "git_commit_create_v",
1018  git_commit_create_v(&Coid, git->R, "HEAD", git->S, git->S,
1019  NULL, "RPM init commit", git->T, 0));
1020  if (xx < 0) {
1021  fprintf(fp, "Could not create the initial commit\n");
1022  goto exit;
1023  }
1024 
1025  xx = 0;
1026 
1027 exit:
1028  if (git->I) {
1029  git_signature_free(git->I);
1030  git->I = NULL;
1031  }
1032  /* Clean up so we don't leak memory */
1033  if (git->T) {
1034  git_tree_free(git->T);
1035  git->T = NULL;
1036  }
1037  if (git->S) {
1038  git_signature_free(git->S);
1039  git->S = NULL;
1040  }
1041  return xx;
1042 }
1043 #endif /* defined(WITH_LIBGIT2) */
1044 
1045 rpmRC rpmgitCmdInit(int argc, char *argv[])
1046 {
1047  int rc = RPMRC_FAIL;
1048 #if defined(WITH_LIBGIT2)
1049  const char * init_template = NULL;
1050  const char * init_shared = NULL;
1051  const char * init_gitdir = NULL;
1052  enum {
1053  _INIT_QUIET = (1 << 0),
1054  _INIT_BARE = (1 << 1),
1055  _INIT_COMMIT = (1 << 2),
1056  };
1057  int init_flags = 0;
1058 #define INIT_ISSET(_a) (init_flags & _INIT_##_a)
1059  struct poptOption initOpts[] = {
1060  { "bare", '\0', POPT_BIT_SET, &init_flags, _INIT_BARE,
1061  N_("Create a bare repository."), NULL },
1062  { "template", '\0', POPT_ARG_STRING, &init_template, 0,
1063  N_("Specify the <template> directory."), N_("<template>") },
1064  /* XXX POPT_ARGFLAG_OPTIONAL */
1065  { "shared", '\0', POPT_ARG_STRING, &init_shared, 0,
1066  N_("Specify how the git repository is to be shared."),
1067  N_("{false|true|umask|group|all|world|everybody|0xxx}") },
1068  { "separate-git-dir", '\0', POPT_ARG_STRING, &init_gitdir, 0,
1069  N_("Specify a separate <gitdir> directory."), N_("<gitdir>") },
1070  { "quiet", 'q', POPT_BIT_SET, &init_flags, _INIT_QUIET,
1071  N_("Quiet mode."), NULL },
1072  { "initial-commit", '\0', POPT_BIT_SET, &init_flags, _INIT_COMMIT,
1073  N_("Initial commit.."), NULL },
1074 
1075  POPT_AUTOALIAS
1076  POPT_AUTOHELP
1077  POPT_TABLEEND
1078  };
1079  rpmgit git = rpmgitNew(argv, 0x80000000, initOpts);
1080 git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
1081  /* XXX git->repodir? */
1082 const char * dir = git->fn; /* XXX */
1083  int xx = -1;
1084  int i;
1085 
1086 if (_rpmgit_debug)
1087 fprintf(stderr, "==> %s(%p[%d]) git %p flags 0x%x\n", __FUNCTION__, argv, argc, git, init_flags);
1088 
1089  /* XXX template? */
1090  /* XXX parse shared to fmode/dmode. */
1091  /* XXX quiet? */
1092  /* XXX separate-git-dir? */
1093  /* XXX initial-commit? */
1094 
1095  /* Impedance match the options. */
1096  opts.flags = GIT_REPOSITORY_INIT_MKPATH;
1097  if (INIT_ISSET(BARE)) {
1098  git->is_bare = 1;
1099  opts.flags |= GIT_REPOSITORY_INIT_BARE;
1100  } else
1101  git->is_bare = 0;
1102  if (init_template) {
1103  opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
1104  opts.template_path = init_template;
1105  }
1106  if (init_gitdir) {
1107  /* XXX use git->fn to eliminate dir. xstrdup? */
1108  opts.workdir_path = dir;
1109  dir = init_gitdir;
1110  }
1111  if (init_shared)
1112  git->shared_umask = parse_shared(init_shared);
1113  else
1114  git->shared_umask = GIT_REPOSITORY_INIT_SHARED_UMASK;
1115 
1116  /* Initialize a git repository. */
1117  xx = rpmgitInit(git, &opts);
1118  if (xx)
1119  goto exit;
1120 
1121  /* XXX elsewhere */
1122  if (!INIT_ISSET(QUIET)) {
1123  if (git->is_bare || init_gitdir)
1124  dir = git_repository_path(git->R);
1125  else
1126  dir = git_repository_workdir(git->R);
1127  printf("Initialized empty Git repository in %s\n", dir);
1128  }
1129 
1130  /* XXX elsewhere */
1131  /* Read/print/save configuration info. */
1132  xx = rpmgitConfig(git);
1133  if (xx)
1134  goto exit;
1135 
1136  /* As an extension to the basic "git init" command, this example
1137  * gives the option to create an empty initial commit. This is
1138  * mostly to demonstrate what it takes to do that, but also some
1139  * people like to have that empty base commit in their repo.
1140  */
1141  if (INIT_ISSET(COMMIT)) {
1142  xx = create_initial_commit(git, git->R);
1143  if (xx)
1144  goto exit;
1145  printf("Created empty initial commit\n");
1146  goto exit;
1147  }
1148 
1149  /* XXX automagic add and commit (for now) */
1150  if (git->ac <= 0)
1151  goto exit;
1152 
1153  /* Create file(s) in _workdir (if any). */
1154  for (i = 0; i < git->ac; i++) {
1155  const char * fn = git->av[i];
1156 
1157  /* Add the file to the repository. */
1158  xx = rpmgitAddFile(git, fn);
1159  if (xx)
1160  goto exit;
1161  }
1162 
1163  /* Commit added files. */
1164  if (git->ac > 0) {
1165  static const char _msg[] = "WDJ commit";
1166  xx = rpmgitCommit(git, _msg);
1167  }
1168  if (xx)
1169  goto exit;
1170 rpmgitPrintCommit(git, git->C, git->fp);
1171 
1172 rpmgitPrintIndex(git->I, git->fp);
1173 rpmgitPrintHead(git, NULL, git->fp);
1174 
1175 exit:
1176  rc = (xx ? RPMRC_FAIL : RPMRC_OK);
1177 SPEW(0, rc, git);
1178  init_gitdir = _free(init_gitdir);
1179  init_shared = _free(init_shared);
1180  init_template = _free(init_template);
1181 
1182  git = rpmgitFree(git);
1183 #endif /* defined(WITH_LIBGIT2) */
1184  return rc;
1185 }
1186 #undef INIT_ISSET
1187 
1188 /*==============================================================*/
1189 
1190 #if defined(WITH_LIBGIT2)
1191 static int print_matched_cb(const char * fn, const char *matched_pathspec,
1192  void * _git)
1193 {
1194  FILE * fp = stdout;
1195  rpmgit git = (rpmgit) _git;
1196  git_status_t status;
1197  int rc = -1; /* XXX assume abort */
1198  int xx;
1199 
1200  (void) matched_pathspec;
1201 
1202  xx = chkgit(git, "git_status_file",
1203  git_status_file((unsigned int *) &status, git->R, fn));
1204  if (xx)
1205  goto exit;
1206 
1207  if (status & GIT_STATUS_WT_MODIFIED || status & GIT_STATUS_WT_NEW) {
1208  fprintf(fp, "add '%s'\n", fn);
1209  rc = 0;
1210  goto exit;
1211  }
1212  rc = 1;
1213 
1214 exit:
1215 SPEW(0, rc, git);
1216  return rc;
1217 }
1218 #endif
1219 
1220 #ifdef REFERENCE
1221 OPTIONS
1222  <filepattern>...
1223  Files to add content from. Fileglobs (e.g. *.c) can be given to
1224  add all matching files. Also a leading directory name (e.g. dir to
1225  add dir/file1 and dir/file2) can be given to add all files in the
1226  directory, recursively.
1227 
1228  -n, --dry-run
1229  Don’t actually add the file(s), just show if they exist.
1230 
1231  -v, --verbose
1232  Be verbose.
1233 
1234  -f, --force
1235  Allow adding otherwise ignored files.
1236 
1237  -i, --interactive
1238  Add modified contents in the working tree interactively to the
1239  index. Optional path arguments may be supplied to limit operation
1240  to a subset of the working tree. See “Interactive mode” for
1241  details.
1242 
1243  -p, --patch
1244  Interactively choose hunks of patch between the index and the work
1245  tree and add them to the index. This gives the user a chance to
1246  review the difference before adding modified contents to the index.
1247 
1248  This effectively runs add --interactive, but bypasses the initial
1249  command menu and directly jumps to the patch subcommand. See
1250  “Interactive mode” for details.
1251 
1252  -e, --edit
1253  Open the diff vs. the index in an editor and let the user edit it.
1254  After the editor was closed, adjust the hunk headers and apply the
1255  patch to the index.
1256 
1257  NOTE: Obviously, if you change anything else than the first
1258  character on lines beginning with a space or a minus, the patch
1259  will no longer apply.
1260 
1261  -u, --update
1262  Only match <filepattern> against already tracked files in the index
1263  rather than the working tree. That means that it will never stage
1264  new files, but that it will stage modified new contents of tracked
1265  files and that it will remove files from the index if the
1266  corresponding files in the working tree have been removed.
1267 
1268  If no <filepattern> is given, default to "."; in other words,
1269  update all tracked files in the current directory and its
1270  subdirectories.
1271 
1272  -A, --all
1273  Like -u, but match <filepattern> against files in the working tree
1274  in addition to the index. That means that it will find new files as
1275  well as staging modified content and removing files that are no
1276  longer in the working tree.
1277 
1278  -N, --intent-to-add
1279  Record only the fact that the path will be added later. An entry
1280  for the path is placed in the index with no content. This is useful
1281  for, among other things, showing the unstaged content of such files
1282  with git diff and committing them with git commit -a.
1283 
1284  --refresh
1285  Don’t add the file(s), but only refresh their stat() information in
1286  the index.
1287 
1288  --ignore-errors
1289  If some files could not be added because of errors indexing them,
1290  do not abort the operation, but continue adding the others. The
1291  command shall still exit with non-zero status.
1292 
1293  --
1294  This option can be used to separate command-line options from the
1295  list of files, (useful when filenames might be mistaken for
1296  command-line options).
1297 #endif
1298 
1299 
1300 rpmRC rpmgitCmdAdd(int argc, char *argv[])
1301 {
1302  int rc = RPMRC_FAIL;
1303 #if defined(WITH_LIBGIT2)
1304  enum {
1305  _ADD_SKIP = (1 << 0),
1306  _ADD_VERBOSE = (1 << 1),
1307  _ADD_FORCE = (1 << 2),
1308  _ADD_INTERACTIVE = (1 << 3),
1309  _ADD_PATCH = (1 << 4),
1310  _ADD_EDIT = (1 << 5),
1311  _ADD_UPDATE = (1 << 6),
1312  _ADD_ALL = (1 << 7),
1313  _ADD_INTENT_TO_ADD = (1 << 8),
1314  _ADD_REFRESH = (1 << 9),
1315  _ADD_IGNORE_ERRORS = (1 << 10),
1316  };
1317  int add_flags = 0;
1318 #define ADD_ISSET(_a) (add_flags & _ADD_##_a)
1319  struct poptOption addOpts[] = {
1320  { "dry-run", 'n', POPT_BIT_SET, &add_flags, _ADD_SKIP,
1321  (""), NULL },
1322  { "verbose", 'v', POPT_BIT_SET, &add_flags, _ADD_VERBOSE,
1323  N_("Verbose mode."), NULL },
1324  { "force", 'f', POPT_BIT_SET, &add_flags, _ADD_FORCE,
1325  (""), NULL },
1326  { "interactive", 'i', POPT_BIT_SET, &add_flags, _ADD_INTERACTIVE,
1327  (""), NULL },
1328  { "patch", 'p', POPT_BIT_SET, &add_flags, _ADD_PATCH,
1329  (""), NULL },
1330  { "edit", 'e', POPT_BIT_SET, &add_flags, _ADD_EDIT,
1331  (""), NULL },
1332  { "update", 'u', POPT_BIT_SET, &add_flags, _ADD_UPDATE,
1333  (""), NULL },
1334  { "all", 'A', POPT_BIT_SET, &add_flags, _ADD_ALL,
1335  (""), NULL },
1336  { "intent-to-add", 'N', POPT_BIT_SET, &add_flags, _ADD_INTENT_TO_ADD,
1337  (""), NULL },
1338  { "refresh", '\0', POPT_BIT_SET, &add_flags, _ADD_REFRESH,
1339  (""), NULL },
1340  { "ignore-errors", '\0', POPT_BIT_SET, &add_flags, _ADD_IGNORE_ERRORS,
1341  (""), NULL },
1342  POPT_AUTOALIAS
1343  POPT_AUTOHELP
1344  POPT_TABLEEND
1345  };
1346  rpmgit git = rpmgitNew(argv, 0, addOpts);
1347  int xx = -1;
1348 
1349  git_index_matched_path_cb matched_cb = NULL;
1350 
1351  git_strarray array = {0};
1352  array.strings = (char **) git->av;
1353  array.count = git->ac;
1354 
1355  if (ADD_ISSET(VERBOSE) || ADD_ISSET(SKIP))
1356  matched_cb = print_matched_cb;
1357 
1358  /* XXX Get the index file for this repository. */
1359  xx = chkgit(git, "git_repository_index",
1360  git_repository_index((git_index **)&git->I, git->R));
1361  if (xx)
1362  goto exit;
1363 
1364 if (_rpmgit_debug < 0) rpmgitPrintIndex(git->I, git->fp);
1365  if (ADD_ISSET(UPDATE))
1366  xx = chkgit(git, "git_index_update_all",
1367  git_index_update_all(git->I, &array, matched_cb, git));
1368  else
1369  xx = chkgit(git, "git_index_add_all",
1370  git_index_add_all(git->I, &array, 0, matched_cb, git));
1371 if (_rpmgit_debug < 0) rpmgitPrintIndex(git->I, git->fp);
1372 
1373  xx = chkgit(git, "git_index_write",
1374  git_index_write(git->I));
1375 
1376 exit:
1377  rc = (xx ? RPMRC_FAIL : RPMRC_OK);
1378 SPEW(0, rc, git);
1379 
1380  if (git->I)
1381  git_index_free(git->I);
1382  git->I = NULL;
1383  git = rpmgitFree(git);
1384 #endif /* defined(WITH_LIBGIT2) */
1385  return rc;
1386 }
1387 #undef ADD_ISSET
1388 
1389 /*==============================================================*/
1390 
1391 rpmRC rpmgitCmdCommit(int argc, char *argv[])
1392 {
1393  int rc = RPMRC_FAIL;
1394 #if defined(WITH_LIBGIT2)
1395  const char * commit_file = NULL; /* XXX argv? */
1396  const char * commit_author = NULL;
1397  const char * commit_date = NULL;
1398  const char * commit_msg = NULL;
1399  const char * commit_template = NULL;
1400  const char * commit_cleanup = NULL;
1401  const char * commit_untracked = NULL;
1402  enum {
1403  _COMMIT_ALL = (1 << 0),
1404  _COMMIT_RESET_AUTHOR = (1 << 1),
1405  _COMMIT_SHORT = (1 << 2),
1406  _COMMIT_PORCELAIN = (1 << 3),
1407  _COMMIT_ZERO = (1 << 4),
1408  _COMMIT_SIGNOFF = (1 << 5),
1409  _COMMIT_NO_VERIFY = (1 << 6),
1410  _COMMIT_ALLOW_EMPTY = (1 << 7),
1411  _COMMIT_EDIT = (1 << 8),
1412  _COMMIT_AMEND = (1 << 9),
1413  _COMMIT_INCLUDE = (1 << 10),
1414  _COMMIT_ONLY = (1 << 11),
1415  _COMMIT_VERBOSE = (1 << 12),
1416  _COMMIT_QUIET = (1 << 13),
1417  _COMMIT_DRY_RUN = (1 << 14),
1418  _COMMIT_STATUS = (1 << 15),
1419  _COMMIT_NO_STATUS = (1 << 16),
1420  };
1421  int commit_flags = 0;
1422 #define COMMIT_ISSET(_a) (commit_flags & _COMMIT_##_a)
1423  struct poptOption commitOpts[] = {
1424  { "all", 'a', POPT_BIT_SET, &commit_flags, _COMMIT_ALL,
1425  (""), NULL },
1426  /* XXX -C */
1427  /* XXX -c */
1428  { "reset-author", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_RESET_AUTHOR,
1429  (""), NULL },
1430  { "short", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_SHORT,
1431  (""), NULL },
1432  { "porcelain", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_PORCELAIN,
1433  (""), NULL },
1434  { NULL, 'z', POPT_BIT_SET, &commit_flags, _COMMIT_ZERO,
1435  (""), NULL },
1436  { "file", 'F', POPT_ARG_STRING, &commit_file, 0,
1437  (""), NULL },
1438  { "author", '\0', POPT_ARG_STRING, &commit_author, 0,
1439  (""), NULL },
1440  { "date", '\0', POPT_ARG_STRING, &commit_date, 0,
1441  (""), NULL },
1442  { "message", 'm', POPT_ARG_STRING, &commit_msg, 0,
1443  (""), NULL },
1444  { "template", 't', POPT_ARG_STRING, &commit_template, 0,
1445  (""), NULL },
1446  { "signoff", 's', POPT_BIT_SET, &commit_flags, _COMMIT_SIGNOFF,
1447  (""), NULL },
1448  { "no-verify", 'n', POPT_BIT_SET, &commit_flags, _COMMIT_NO_VERIFY,
1449  (""), NULL },
1450  { "allow-empty", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_ALLOW_EMPTY,
1451  (""), NULL },
1452  { "cleanup", '\0', POPT_ARG_STRING, &commit_cleanup, 0,
1453  (""), NULL },
1454  { "edit", 'e', POPT_BIT_SET, &commit_flags, _COMMIT_EDIT,
1455  (""), NULL },
1456  { "amend", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_AMEND,
1457  (""), NULL },
1458  { "include", 'i', POPT_BIT_SET, &commit_flags, _COMMIT_INCLUDE,
1459  (""), NULL },
1460  { "only", 'o', POPT_BIT_SET, &commit_flags, _COMMIT_ONLY,
1461  (""), NULL },
1462  { "untracked", 'u', POPT_ARG_STRING, &commit_untracked, 0,
1463  (""), NULL },
1464  { "verbose", 'v', POPT_BIT_SET, &commit_flags, _COMMIT_VERBOSE,
1465  N_("Verbose mode."), NULL },
1466  { "quiet", 'q', POPT_BIT_SET, &commit_flags, _COMMIT_QUIET,
1467  N_("Quiet mode."), NULL },
1468  { "dry-run", '\n', POPT_BIT_SET, &commit_flags, _COMMIT_DRY_RUN,
1469  (""), NULL },
1470 
1471  { "status", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_STATUS,
1472  (""), NULL },
1473  { "no-status", '\0', POPT_BIT_SET, &commit_flags, _COMMIT_NO_STATUS,
1474  (""), NULL },
1475 
1476  POPT_AUTOALIAS
1477  POPT_AUTOHELP
1478  POPT_TABLEEND
1479  };
1480  rpmgit git = rpmgitNew(argv, 0, commitOpts);
1481  int xx = -1;
1482 
1483  /* XXX Get the index file for this repository. */
1484  xx = chkgit(git, "git_repository_index",
1485  git_repository_index((git_index **)&git->I, git->R));
1486  if (xx)
1487  goto exit;
1488 
1489  /* Commit changes. */
1490  if (commit_msg == NULL) commit_msg = xstrdup("WDJ commit"); /* XXX */
1491  xx = rpmgitCommit(git, commit_msg);
1492  if (xx)
1493  goto exit;
1494 rpmgitPrintCommit(git, git->C, git->fp);
1495 
1496 rpmgitPrintIndex(git->I, git->fp);
1497 rpmgitPrintHead(git, NULL, git->fp);
1498 
1499 exit:
1500  rc = (xx ? RPMRC_FAIL : RPMRC_OK);
1501 SPEW(0, rc, git);
1502  commit_file = _free(commit_file); /* XXX argv? */
1503  commit_author = _free(commit_author);
1504  commit_date = _free(commit_date);
1505  commit_msg = _free(commit_msg);
1506  commit_template = _free(commit_template);
1507  commit_cleanup = _free(commit_cleanup);
1508  commit_untracked = _free(commit_untracked);
1509 
1510  git = rpmgitFree(git);
1511 #endif /* defined(WITH_LIBGIT2) */
1512  return rc;
1513 }
1514 #undef COMMIT_ISSET
1515 
1516 /*==============================================================*/
1517 
1518 #if defined(WITH_LIBGIT2)
1519 static int diff_output( const git_diff_delta *d, const git_diff_hunk *h,
1520  const git_diff_line *l, void *p)
1521 {
1522  FILE *fp = (FILE*)p;
1523  size_t nw;
1524 
1525  (void)d; (void)h;
1526 
1527  if (fp == NULL)
1528  fp = stdout;
1529 
1530  if (l->origin == GIT_DIFF_LINE_CONTEXT ||
1531  l->origin == GIT_DIFF_LINE_ADDITION ||
1532  l->origin == GIT_DIFF_LINE_DELETION)
1533  fputc(l->origin, fp);
1534 
1535  nw = fwrite(l->content, 1, l->content_len, fp);
1536 
1537  return 0;
1538 }
1539 
1540 static int treeish_to_tree(git_tree ** tree, git_repository * repo,
1541  const char * treeish)
1542 {
1543  git_object *obj = NULL;
1544  int err = 0;
1545 
1546  if ((err = git_revparse_single(&obj, repo, treeish)) < 0)
1547  return err;
1548 
1549  if ((err = git_object_peel((git_object **)tree, obj, GIT_OBJ_TREE)) < 0)
1550  return err;
1551 
1552  git_object_free(obj);
1553 
1554  return err;
1555 }
1556 
1557 static char *colors[] = {
1558  "\033[m", /* reset */
1559  "\033[1m", /* bold */
1560  "\033[31m", /* red */
1561  "\033[32m", /* green */
1562  "\033[36m" /* cyan */
1563 };
1564 
1565 enum {
1566  OUTPUT_DIFF = (1 << 0),
1567  OUTPUT_STAT = (1 << 1),
1568  OUTPUT_SHORTSTAT = (1 << 2),
1569  OUTPUT_NUMSTAT = (1 << 3),
1570  OUTPUT_SUMMARY = (1 << 4)
1571 };
1572 
1573 enum {
1574  CACHE_NORMAL = 0,
1575  CACHE_ONLY = 1,
1576  CACHE_NONE = 2
1577 };
1578 
1580 struct opts {
1581  git_diff_options diffopts;
1582  git_diff_find_options findopts;
1583  int color;
1584  int cache;
1585  int output;
1586  git_diff_format_t format;
1587  const char *treeish1;
1588  const char *treeish2;
1589  const char *dir;
1590 };
1591 
1592 static int color_printer(
1593  const git_diff_delta *delta,
1594  const git_diff_hunk *hunk,
1595  const git_diff_line *line,
1596  void * data)
1597 {
1598  int *last_color = data;
1599  int color = 0;
1600 
1601  if (*last_color >= 0) {
1602  switch (line->origin) {
1603  case GIT_DIFF_LINE_ADDITION: color = 3; break;
1604  case GIT_DIFF_LINE_DELETION: color = 2; break;
1605  case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
1606  case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
1607  case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
1608  case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
1609  default: break;
1610  }
1611  if (color != *last_color) {
1612  if (*last_color == 1 || color == 1)
1613  fputs(colors[0], stdout);
1614  fputs(colors[color], stdout);
1615  *last_color = color;
1616  }
1617  }
1618 
1619  return diff_output(delta, hunk, line, stdout);
1620 }
1621 
1623 static void diff_print_stats(rpmgit git, git_diff *diff, struct opts *o)
1624 {
1625  git_diff_stats *stats;
1626  git_buf b = GIT_BUF_INIT_CONST(NULL, 0);
1627  git_diff_stats_format_t format = 0;
1628  int xx;
1629 
1630  xx = chkgit(git, "git_diff_get_stats",
1631  git_diff_get_stats(&stats, diff));
1632 
1633  if (o->output & OUTPUT_STAT)
1634  format |= GIT_DIFF_STATS_FULL;
1635  if (o->output & OUTPUT_SHORTSTAT)
1636  format |= GIT_DIFF_STATS_SHORT;
1637  if (o->output & OUTPUT_NUMSTAT)
1638  format |= GIT_DIFF_STATS_NUMBER;
1639  if (o->output & OUTPUT_SUMMARY)
1640  format |= GIT_DIFF_STATS_INCLUDE_SUMMARY;
1641 
1642  xx = chkgit(git, "git_diff_stats_to_buf",
1643  git_diff_stats_to_buf(&b, stats, format, 80));
1644 
1645  fputs(b.ptr, stdout);
1646 
1647  git_buf_free(&b);
1648  git_diff_stats_free(stats);
1649 }
1650 
1651 #endif /* defined(WITH_LIBGIT2) */
1652 
1653 rpmRC rpmgitCmdDiff(int argc, char *argv[])
1654 {
1655  int rc = RPMRC_FAIL;
1656 #if defined(WITH_LIBGIT2)
1657  struct opts o = {
1658  GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
1659  -1, 0, OUTPUT_DIFF, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
1660  };
1661  enum { /* XXX FIXME */
1662  _DIFF_ALL = (1 << 0),
1663  _DIFF_ZERO = (1 << 1),
1664  };
1665  int diff_flags = 0;
1666 #define DIFF_ISSET(_a) (o.diffopts.flags & GIT_DIFF_##_a)
1667  struct poptOption diffOpts[] = {
1668  /* XXX -u */
1669  { "patch", 'p', POPT_ARG_VAL, &o.format, GIT_DIFF_FORMAT_PATCH,
1670  N_("Generate patch."), NULL },
1671 
1672  { "cached", '\0', POPT_ARG_VAL, &o.cache, CACHE_ONLY,
1673  NULL, NULL },
1674  { "nocache", '\0', POPT_ARG_VAL, &o.cache, CACHE_NONE,
1675  NULL, NULL },
1676 
1677  /* XXX --format=diff-index */
1678  /* XXX --format=raw */
1679  { "raw", '\0', POPT_ARG_VAL, &o.format, GIT_DIFF_FORMAT_RAW,
1680  (""), NULL },
1681  /* XXX --patch-with-raw */
1682  /* XXX --patch-with-stat */
1683  { NULL, 'z', POPT_BIT_SET, &diff_flags, _DIFF_ZERO,
1684  (""), NULL },
1685  /* XXX --format=name */
1686  { "name-only", '\0', POPT_ARG_VAL, &o.format, GIT_DIFF_FORMAT_NAME_ONLY,
1687  N_("Show only names of changed files."), NULL },
1688  /* XXX --format=name-status */
1689  { "name-status", '\0', POPT_ARG_VAL, &o.format, GIT_DIFF_FORMAT_NAME_STATUS,
1690  N_("Show only names and status of changed files."), NULL },
1691  /* XXX --submodule */
1692 
1693  { "color", '\0', POPT_ARG_VAL, &o.color, 0,
1694  N_("Show colored diff."), NULL },
1695  { "no-color", '\0', POPT_ARG_VAL, &o.color, -1,
1696  N_("Turn off colored diff."), NULL },
1697  /* XXX --color-words */
1698  /* XXX --no-renames */
1699  /* XXX --check */
1700  /* XXX --full-index */
1701  /* XXX --binary */
1702  /* XXX --diff-filter */
1703  /* XXX -l */
1704  /* XXX -S */
1705  /* XXX --pickaxe-all */
1706  /* XXX --pickaxe-regex */
1707  /* XXX -O */
1708  { NULL, 'R', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_REVERSE,
1709  N_("Swap two inputs."), NULL },
1710  /* XXX --relative */
1711  { "text", 'a', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_FORCE_TEXT,
1712  N_("Treat all files as text."), NULL },
1713  { "ignore-space-at-eol", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_IGNORE_WHITESPACE_EOL,
1714  N_("Ignore changes in whitespace at EOL."), NULL },
1715  { "ignore-space-change", 'b', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_IGNORE_WHITESPACE_CHANGE,
1716  N_("Ignore changes in amount of whitespace."), NULL },
1717  { "ignore-all-space", 'w', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_IGNORE_WHITESPACE,
1718  N_("Ignore whitespace when comparing lines."), NULL },
1719  { "ignored", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_INCLUDE_IGNORED,
1720  NULL, NULL },
1721  { "untracked", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_INCLUDE_UNTRACKED,
1722  NULL, NULL },
1723  { "patience", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_PATIENCE,
1724  N_("Generate a diff using the \"patience diff\" algorithm."), NULL },
1725  { "minimal", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_MINIMAL,
1726  N_("Generate a minimal diff."), NULL },
1727 
1728  { "stat", '\0', POPT_BIT_SET, &o.output, OUTPUT_STAT,
1729  NULL, NULL },
1730  { "numstat", '\0', POPT_BIT_SET, &o.output, OUTPUT_NUMSTAT,
1731  NULL, NULL },
1732  { "shortstat", '\0', POPT_BIT_SET, &o.output, OUTPUT_SHORTSTAT,
1733  NULL, NULL },
1734  /* XXX --dirstat */
1735  /* XXX --dirstat-by-file */
1736  { "summary", '\0', POPT_ARG_VAL, &o.output, OUTPUT_SUMMARY,
1737  NULL, NULL },
1738 
1739  { "find-renames", 'M', POPT_ARG_SHORT, &o.findopts.rename_threshold, 0,
1740  (""), NULL },
1741  { "find-copies", 'C', POPT_ARG_SHORT, &o.findopts.copy_threshold, 0,
1742  (""), NULL },
1743  { "find-copies-harder", '\0', POPT_BIT_SET, &o.findopts.flags, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED,
1744  (""), NULL },
1745  /* TODO: parse thresholds */
1746  { "break-rewrites", 'B', POPT_BIT_SET, &o.findopts.flags, GIT_DIFF_FIND_REWRITES,
1747  (""), NULL },
1748  { "unified", 'U', POPT_ARG_SHORT, &o.diffopts.context_lines, 0,
1749  N_("Generate diffs with <n> lines of context."), N_("<n>") },
1750 
1751  { "inter-hunk-context", '\0', POPT_ARG_SHORT, &o.diffopts.interhunk_lines, 0,
1752  N_("Show the context between diff hunks."), N_("<lines>") },
1753  /* XXX --abbrev */
1754  { "abbrev", '\0', POPT_ARG_SHORT, &o.diffopts.interhunk_lines, 0,
1755  (""), N_("<XXX>") },
1756 
1757  /* XXX --exit-code */
1758  /* XXX --quiet */
1759  /* XXX --ext-diff */
1760  /* XXX --no-ext-diff */
1761  { "ignore-submodules", '\0', POPT_BIT_SET, &o.diffopts.flags, GIT_DIFF_IGNORE_SUBMODULES,
1762  N_("Ignore changes to submodules in the diff generation."), NULL },
1763  { "src-prefix", '\0', POPT_ARG_STRING, &o.diffopts.old_prefix, 0,
1764  N_("Show the given source <prefix> instead of \"a/\"."), N_("<prefix>") },
1765  { "dst-prefix", '\0', POPT_ARG_STRING, &o.diffopts.new_prefix, 0,
1766  N_("Show the given destination prefix instead of \"b/\"."), N_("<prefix>") },
1767  /* XXX --no-prefix */
1768  /* XXX --git-dir */
1769 
1770  /* XXX --untracked-dirs? */
1771 
1772  POPT_AUTOALIAS
1773  POPT_AUTOHELP
1774  POPT_TABLEEND
1775  };
1776  rpmgit git = NULL; /* XXX rpmgitNew(argv, 0, diffOpts); */
1777 git_diff * diff = NULL;
1778 const char * treeish1 = NULL;
1779 git_tree *t1 = NULL;
1780 const char * treeish2 = NULL;
1781 git_tree *t2 = NULL;
1782  int xx = -1;
1783 
1784 #ifdef NOTYET
1785 const char * dir = ".";
1786 char path[GIT_PATH_MAX];
1787 const char * fn;
1788  xx = chkgit(git, "git_repository_discover",
1789  git_repository_discover(path, sizeof(path), dir, 0, "/"));
1790  if (xx) {
1791  fprintf(stderr, "Could not discover repository\n");
1792  goto exit;
1793  }
1794  fn = path;
1795 #endif
1796 
1797  if (o.findopts.rename_threshold)
1798  o.findopts.flags |= GIT_DIFF_FIND_RENAMES;
1799  if (o.findopts.copy_threshold)
1800  o.findopts.flags |= GIT_DIFF_FIND_COPIES;
1801 
1802  git = rpmgitNew(argv, 0, diffOpts);
1803 
1804  treeish1 = (git->ac >= 1 ? git->av[0] : NULL);
1805  treeish2 = (git->ac >= 2 ? git->av[1] : NULL);
1806 
1807  if (treeish1) {
1808  xx = chkgit(git, "treeish_to_tree",
1809  treeish_to_tree(&t1, git->R, treeish1));
1810  if (xx) {
1811  fprintf(stderr, "Looking up first tree\n");
1812  goto exit;
1813  }
1814  }
1815  if (treeish2) {
1816  xx = chkgit(git, "treeish_to_tree",
1817  treeish_to_tree(&t2, git->R, treeish2));
1818  if (xx) {
1819  fprintf(stderr, "Looking up second tree\n");
1820  goto exit;
1821  }
1822  }
1823 
1824  /* <sha1> <sha2> */
1825  /* <sha1> --cached */
1826  /* <sha1> */
1827  /* --cached */
1828  /* nothing */
1829 
1830  if (t1 && t2)
1831  xx = chkgit(git, "git_diff_tree_to_tree",
1832  git_diff_tree_to_tree(&diff, git->R, t1, t2, &o.diffopts));
1833  else if (o.cache != CACHE_NORMAL) {
1834  if (t1 == NULL)
1835  xx = chkgit(git, "treeish_to_tree",
1836  treeish_to_tree(&t1, git->R, "HEAD"));
1837 
1838  if (o.cache == CACHE_NONE)
1839  xx = chkgit(git, "git_diff_tree_to_workdir",
1840  git_diff_tree_to_workdir(&diff, git->R, t1, &o.diffopts));
1841  else
1842  xx = chkgit(git, "git_diff_tree_to_index",
1843  git_diff_tree_to_index(&diff, git->R, t1, NULL, &o.diffopts));
1844  }
1845  else if (t1)
1846  xx = chkgit(git, "git_diff_tree_to_index",
1847  git_diff_tree_to_index(&diff, git->R, t1, NULL, &o.diffopts));
1848  else if (t1)
1849  xx = chkgit(git, "git_diff_tree_to_workdir_with_index",
1850  git_diff_tree_to_workdir_with_index(&diff, git->R, t1, &o.diffopts));
1851  else
1852  xx = chkgit(git, "git_diff_index_to_workdir",
1853  git_diff_index_to_workdir(&diff, git->R, NULL, &o.diffopts));
1854 
1856  if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
1857  xx = chkgit(git, "git_diff_find_similar",
1858  git_diff_find_similar(diff, &o.findopts));
1859 
1862  if (o.output != OUTPUT_DIFF)
1863  diff_print_stats(git, diff, &o);
1864 
1865  if (o.output & OUTPUT_DIFF) {
1866  if (o.color >= 0)
1867  fputs(colors[0], stdout);
1868 
1869  xx = chkgit(git, "git_diff_print",
1870  git_diff_print(diff, o.format, color_printer, &o.color));
1871 
1872  if (o.color >= 0)
1873  fputs(colors[0], stdout);
1874  }
1875 
1876 exit:
1877  rc = (xx ? RPMRC_FAIL : RPMRC_OK);
1878 SPEW(0, rc, git);
1879 
1881  if (diff)
1882  git_diff_free(diff);
1883  if (t1)
1884  git_tree_free(t1);
1885  if (t2)
1886  git_tree_free(t2);
1887 
1888  git = rpmgitFree(git);
1889 #endif /* defined(WITH_LIBGIT2) */
1890  return rc;
1891 }
1892 #undef DIFF_ISSET
1893 
1894 /*==============================================================*/
1895 
1896 #if defined(WITH_LIBGIT2)
1897 enum {
1898  FORMAT_DEFAULT = 0,
1899  FORMAT_LONG = 1,
1900  FORMAT_SHORT = 2,
1901  FORMAT_PORCELAIN = 3,
1902 };
1903 
1904 static void show_branch(git_repository *repo, int format)
1905 {
1906  int error = 0;
1907  const char *branch = NULL;
1908  git_reference *head = NULL;
1909 
1910  error = git_repository_head(&head, repo);
1911 
1912  if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
1913  branch = NULL;
1914  else if (!error)
1915  branch = git_reference_shorthand(head);
1916  else
1917  check(error, "failed to get current branch", NULL);
1918 
1919  if (format == FORMAT_LONG)
1920  printf("# On branch %s\n",
1921  branch ? branch : "Not currently on any branch.");
1922  else
1923  printf("## %s\n", branch ? branch : "HEAD (no branch)");
1924 
1925  git_reference_free(head);
1926 }
1927 
1928 static void print_long(git_repository *repo, git_status_list *status)
1929 {
1930  size_t maxi = git_status_list_entrycount(status);
1931  size_t i;
1932  const git_status_entry *s;
1933  int header = 0;
1934  int changes_in_index = 0;
1935  int changed_in_workdir = 0;
1936  int rm_in_workdir = 0;
1937  const char *old_path;
1938  const char *new_path;
1939 
1940  (void)repo;
1941 
1942  /* print index changes */
1943 
1944  for (i = 0; i < maxi; ++i) {
1945  char *istatus = NULL;
1946 
1947  s = git_status_byindex(status, i);
1948 
1949  if (s->status == GIT_STATUS_CURRENT)
1950  continue;
1951 
1952  if (s->status & GIT_STATUS_WT_DELETED)
1953  rm_in_workdir = 1;
1954 
1955  if (s->status & GIT_STATUS_INDEX_NEW)
1956  istatus = "new file: ";
1957  if (s->status & GIT_STATUS_INDEX_MODIFIED)
1958  istatus = "modified: ";
1959  if (s->status & GIT_STATUS_INDEX_DELETED)
1960  istatus = "deleted: ";
1961  if (s->status & GIT_STATUS_INDEX_RENAMED)
1962  istatus = "renamed: ";
1963  if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
1964  istatus = "typechange:";
1965 
1966  if (istatus == NULL)
1967  continue;
1968 
1969  if (!header) {
1970  printf("# Changes to be committed:\n");
1971  printf("# (use \"git reset HEAD <file>...\" to unstage)\n");
1972  printf("#\n");
1973  header = 1;
1974  }
1975 
1976  old_path = s->head_to_index->old_file.path;
1977  new_path = s->head_to_index->new_file.path;
1978 
1979  if (old_path && new_path && strcmp(old_path, new_path))
1980  printf("#\t%s %s -> %s\n", istatus, old_path, new_path);
1981  else
1982  printf("#\t%s %s\n", istatus, old_path ? old_path : new_path);
1983  }
1984 
1985  if (header) {
1986  changes_in_index = 1;
1987  printf("#\n");
1988  }
1989 
1990  /* Print workdir changes to tracked files */
1991  header = 0;
1992  for (i = 0; i < maxi; ++i) {
1993  char *wstatus = NULL;
1994 
1995  s = git_status_byindex(status, i);
1996 
1997  /*
1998  * With `GIT_STATUS_OPT_INCLUDE_UNMODIFIED` (not used in this example)
1999  * `index_to_workdir` may not be `NULL` even if there are
2000  * no differences, in which case it will be a `GIT_DELTA_UNMODIFIED`.
2001  */
2002  if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
2003  continue;
2004 
2005  /* Print out the output since we know the file has some changes */
2006 
2007  if (s->status & GIT_STATUS_WT_MODIFIED)
2008  wstatus = "modified: ";
2009  if (s->status & GIT_STATUS_WT_DELETED)
2010  wstatus = "deleted: ";
2011  if (s->status & GIT_STATUS_WT_RENAMED)
2012  wstatus = "renamed: ";
2013  if (s->status & GIT_STATUS_WT_TYPECHANGE)
2014  wstatus = "typechange:";
2015 
2016  if (wstatus == NULL)
2017  continue;
2018 
2019  if (!header) {
2020  printf("# Changes not staged for commit:\n");
2021  printf("# (use \"git add%s <file>...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : "");
2022  printf("# (use \"git checkout -- <file>...\" to discard changes in working directory)\n");
2023  printf("#\n");
2024  header = 1;
2025  }
2026 
2027  old_path = s->index_to_workdir->old_file.path;
2028  new_path = s->index_to_workdir->new_file.path;
2029 
2030  if (old_path && new_path && strcmp(old_path, new_path))
2031  printf("#\t%s %s -> %s\n", wstatus, old_path, new_path);
2032  else
2033  printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path);
2034  }
2035 
2036  if (header) {
2037  changed_in_workdir = 1;
2038  printf("#\n");
2039  }
2040 
2041  /* Print untracked files */
2042  header = 0;
2043  for (i = 0; i < maxi; ++i) {
2044  s = git_status_byindex(status, i);
2045 
2046  if (s->status == GIT_STATUS_WT_NEW) {
2047 
2048  if (!header) {
2049  printf("# Untracked files:\n");
2050  printf("# (use \"git add <file>...\" to include in what will be committed)\n");
2051  printf("#\n");
2052  header = 1;
2053  }
2054 
2055  printf("#\t%s\n", s->index_to_workdir->old_file.path);
2056  }
2057  }
2058 
2059  /* Print ignored files */
2060  header = 0;
2061  for (i = 0; i < maxi; ++i) {
2062  s = git_status_byindex(status, i);
2063 
2064  if (s->status == GIT_STATUS_IGNORED) {
2065 
2066  if (!header) {
2067  printf("# Ignored files:\n");
2068  printf("# (use \"git add -f <file>...\" to include in what will be committed)\n");
2069  printf("#\n");
2070  header = 1;
2071  }
2072 
2073  printf("#\t%s\n", s->index_to_workdir->old_file.path);
2074  }
2075  }
2076 
2077  if (!changes_in_index && changed_in_workdir)
2078  printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
2079 }
2080 
2081 static void print_short(git_repository *repo, git_status_list *status)
2082 {
2083  size_t maxi = git_status_list_entrycount(status);
2084  size_t i;
2085  const git_status_entry *s;
2086  char istatus;
2087  char wstatus;
2088  const char *extra;
2089  const char *a;
2090  const char *b;
2091  const char *c;
2092 
2093  for (i = 0; i < maxi; ++i) {
2094  s = git_status_byindex(status, i);
2095 
2096  if (s->status == GIT_STATUS_CURRENT)
2097  continue;
2098 
2099  a = b = c = NULL;
2100  istatus = wstatus = ' ';
2101  extra = "";
2102 
2103  if (s->status & GIT_STATUS_INDEX_NEW)
2104  istatus = 'A';
2105  if (s->status & GIT_STATUS_INDEX_MODIFIED)
2106  istatus = 'M';
2107  if (s->status & GIT_STATUS_INDEX_DELETED)
2108  istatus = 'D';
2109  if (s->status & GIT_STATUS_INDEX_RENAMED)
2110  istatus = 'R';
2111  if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
2112  istatus = 'T';
2113 
2114  if (s->status & GIT_STATUS_WT_NEW) {
2115  if (istatus == ' ')
2116  istatus = '?';
2117  wstatus = '?';
2118  }
2119  if (s->status & GIT_STATUS_WT_MODIFIED)
2120  wstatus = 'M';
2121  if (s->status & GIT_STATUS_WT_DELETED)
2122  wstatus = 'D';
2123  if (s->status & GIT_STATUS_WT_RENAMED)
2124  wstatus = 'R';
2125  if (s->status & GIT_STATUS_WT_TYPECHANGE)
2126  wstatus = 'T';
2127 
2128  if (s->status & GIT_STATUS_IGNORED) {
2129  istatus = '!';
2130  wstatus = '!';
2131  }
2132 
2133  if (istatus == '?' && wstatus == '?')
2134  continue;
2135 
2136  /*
2137  * A commit in a tree is how submodules are stored, so
2138  * let's go take a look at its status.
2139  */
2140  if (s->index_to_workdir &&
2141  s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
2142  {
2143  git_submodule *sm = NULL;
2144  unsigned int smstatus = 0;
2145 
2146  if (!git_submodule_lookup( &sm, repo, s->index_to_workdir->new_file.path)
2147  && !git_submodule_status(&smstatus, sm))
2148  {
2149  if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
2150  extra = " (new commits)";
2151  else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
2152  extra = " (modified content)";
2153  else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
2154  extra = " (modified content)";
2155  else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
2156  extra = " (untracked content)";
2157  }
2158  git_submodule_free(sm);
2159  }
2160 
2161  /*
2162  * Now that we have all the information, format the output.
2163  */
2164  if (s->head_to_index) {
2165  a = s->head_to_index->old_file.path;
2166  b = s->head_to_index->new_file.path;
2167  }
2168  if (s->index_to_workdir) {
2169  if (!a)
2170  a = s->index_to_workdir->old_file.path;
2171  if (!b)
2172  b = s->index_to_workdir->old_file.path;
2173  c = s->index_to_workdir->new_file.path;
2174  }
2175 
2176  if (istatus == 'R') {
2177  if (wstatus == 'R')
2178  printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra);
2179  else
2180  printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra);
2181  } else {
2182  if (wstatus == 'R')
2183  printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra);
2184  else
2185  printf("%c%c %s%s\n", istatus, wstatus, a, extra);
2186  }
2187  }
2188 
2189  for (i = 0; i < maxi; ++i) {
2190  s = git_status_byindex(status, i);
2191 
2192  if (s->status == GIT_STATUS_WT_NEW)
2193  printf("?? %s\n", s->index_to_workdir->old_file.path);
2194  }
2195 }
2196 
2197 static int print_submod(git_submodule *sm, const char *name, void *payload)
2198 {
2199  int *count = payload;
2200  (void)name;
2201 
2202  if (*count == 0)
2203  printf("# Submodules\n");
2204  (*count)++;
2205 
2206  printf("# - submodule '%s' at %s\n",
2207  git_submodule_name(sm), git_submodule_path(sm));
2208 
2209  return 0;
2210 }
2211 
2212 #endif /* defined(WITH_LIBGIT2) */
2213 
2214 rpmRC rpmgitCmdStatus(int argc, char *argv[])
2215 {
2216  int rc = RPMRC_FAIL;
2217 #if defined(WITH_LIBGIT2)
2218  git_status_options opt = { GIT_STATUS_OPTIONS_VERSION, 0, 0, { NULL, 0} };
2219  const char * status_untracked_files = xstrdup("all");
2220  const char * status_ignore_submodules = xstrdup("all");
2221  enum {
2222  _STATUS_BRANCH = (1 << 0),
2223  _STATUS_ZERO = (1 << 1),
2224  _STATUS_IGNORED = (1 << 2),
2225  _STATUS_SHOWSUBMOD = (1 << 3),
2226  };
2227  int status_flags = 0;
2228 #define STATUS_ISSET(_a) (status_flags & _STATUS_##_a)
2229  int format = FORMAT_DEFAULT; /* XXX git->format? */
2230  int repeat = 0;
2231  struct poptOption statusOpts[] = {
2232  { "short", 's', POPT_ARG_VAL, &format, FORMAT_SHORT,
2233  N_("Give the output in the short-format."), NULL },
2234  { "long", '\0', POPT_ARG_VAL, &format, FORMAT_LONG,
2235  N_("Give the output in the long-format."), NULL },
2236  { "porcelain", '\0', POPT_ARG_VAL, &format, FORMAT_PORCELAIN,
2237  N_("Give the output in a stable, easy-to-parse format for scripts."), NULL },
2238  { "branch", 'b', POPT_BIT_SET, &status_flags, _STATUS_BRANCH,
2239  N_("."), NULL },
2240  { NULL, 'z', POPT_BIT_SET, &status_flags, _STATUS_ZERO,
2241  N_("."), NULL },
2242  { "ignored", '\0', POPT_BIT_SET, &status_flags, _STATUS_IGNORED,
2243  N_("."), NULL },
2244  /*XXX -uno */
2245  /*XXX -unormal */
2246  /*XXX -uall */
2247  { "untracked-files", 'u', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &status_untracked_files, 0,
2248  N_("Show untracked files."), N_("{no|normal|all}") },
2249  { "ignore-submodules", '\0', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &status_ignore_submodules, 0,
2250  N_("Ignore sub-modules."), N_("{all}") },
2251  { "repeat", '\0', POPT_ARG_INT, &repeat, 0,
2252  N_("Repeat every <sec> seconds."), N_("<sec>") },
2253  { "list-submodules", '\0', POPT_BIT_SET, &status_flags, _STATUS_SHOWSUBMOD,
2254  N_("."), NULL },
2255  /* --git-dir */
2256  POPT_AUTOALIAS
2257  POPT_AUTOHELP
2258  POPT_TABLEEND
2259  };
2260  rpmgit git = rpmgitNew(argv, 0, statusOpts);
2261  git_status_list * list = NULL;
2262  int xx = -1;
2263 
2264  opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2265 
2266  opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2267  opt.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
2268  opt.flags |= GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
2269 
2270  if (STATUS_ISSET(IGNORED)) {
2271  opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
2272  }
2273  if (!strcmp(status_untracked_files, "no")) {
2274  opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2275  opt.flags &= ~GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
2276  } else
2277  if (!strcmp(status_untracked_files, "normal")) {
2278  opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2279  opt.flags &= ~GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
2280  } else
2281  if (!strcmp(status_untracked_files, "all")) {
2282  opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2283  opt.flags |= GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
2284  }
2285  if (!strcmp(status_ignore_submodules, "all")) {
2286  opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
2287  }
2288 
2289  if (format == FORMAT_DEFAULT)
2290  format = STATUS_ISSET(ZERO) ? FORMAT_PORCELAIN : FORMAT_LONG;
2291  if (format == FORMAT_LONG)
2292  status_flags |= _STATUS_BRANCH;
2293  if (git->ac > 0) {
2294  opt.pathspec.strings = (char **) git->av;
2295  opt.pathspec.count = git->ac;
2296  }
2297 
2298 show_status:
2299  if (repeat)
2300  printf("\033[H\033[2J");
2301 
2302  /*
2303  * Run status on the repository
2304  *
2305  * We use `git_status_list_new()` to generate a list of status
2306  * information which lets us iterate over it at our
2307  * convenience and extract the data we want to show out of
2308  * each entry.
2309  *
2310  * You can use `git_status_foreach()` or
2311  * `git_status_foreach_ext()` if you'd prefer to execute a
2312  * callback for each entry. The latter gives you more control
2313  * about what results are presented.
2314  */
2315 
2316  git->state = 0;
2317  xx = chkgit(git, "git_status_list_new",
2318  git_status_list_new(&list, git->R, &opt));
2319 
2320  if (STATUS_ISSET(BRANCH))
2321  show_branch(git->R, format);
2322 
2323  if (STATUS_ISSET(SHOWSUBMOD)) {
2324  int submod_count = 0;
2325  xx = chkgit(git, "git_submodule_foreach",
2326  git_submodule_foreach(git->R, print_submod, &submod_count));
2327  }
2328 
2329  if (format == FORMAT_LONG)
2330  print_long(git->R, list);
2331  else
2332  print_short(git->R, list);
2333 
2334  git_status_list_free(list);
2335  list = NULL;
2336  if (repeat) {
2337  sleep(repeat);
2338  goto show_status;
2339  }
2340 
2341  goto exit; /* XXX GCC warning */
2342 
2343 exit:
2344  rc = (xx ? RPMRC_FAIL : RPMRC_OK);
2345 SPEW(0, rc, git);
2346 
2347  if (list) {
2348  git_status_list_free(list);
2349  list = NULL;
2350  }
2351  status_untracked_files = _free(status_untracked_files);
2352  status_ignore_submodules = _free(status_ignore_submodules);
2353 
2354  git = rpmgitFree(git);
2355 #endif /* defined(WITH_LIBGIT2) */
2356  return rc;
2357 }
2358 #undef STATUS_ISSET
2359 
2360 /*==============================================================*/
2361 
2362 #define ARGMINMAX(_min, _max) (int)(((_min) << 8) | ((_max) & 0xff))
2363 
2364 static struct poptOption rpmgitCmds[] = {
2365 /* --- PORCELAIN */
2366  { "add", '\0', POPT_ARG_MAINCALL, rpmgitCmdAdd, ARGMINMAX(1,0),
2367  N_("Add file contents to the index."), NULL },
2368 #ifdef NOTYET
2369  { "bisect", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2370  N_("Find by binary search the change that introduced a bug."), NULL },
2371  { "branch", '\0', POPT_ARG_MAINCALL, cmd_branch, ARGMINMAX(0,0),
2372  N_("List, create, or delete branches."), NULL },
2373  { "checkout", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2374  N_("Checkout a branch or paths to the working tree."), NULL },
2375  { "clone", '\0', POPT_ARG_MAINCALL, cmd_clone, ARGMINMAX(0,0),
2376  N_("Clone a repository into a new directory."), NULL },
2377 #endif /* NOTYET */
2378  { "commit", '\0', POPT_ARG_MAINCALL, rpmgitCmdCommit, ARGMINMAX(0,0),
2379  N_("Record changes to the repository."), NULL },
2380  { "diff", '\0', POPT_ARG_MAINCALL, rpmgitCmdDiff, ARGMINMAX(0,0),
2381  N_("Show changes between commits, commit and working tree, etc."), NULL },
2382 #ifdef NOTYET
2383  { "fetch", '\0', POPT_ARG_MAINCALL, cmd_fetch, ARGMINMAX(0,0),
2384  N_("Download objects and refs from another repository."), NULL },
2385  { "grep", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2386  N_("Print lines matching a pattern."), NULL },
2387 #endif /* NOTYET */
2388  /* XXX maxargs=1 */
2389  { "init", '\0', POPT_ARG_MAINCALL, rpmgitCmdInit, ARGMINMAX(1,0),
2390  N_("Create an empty git repository or reinitialize an existing one."), NULL },
2391 #ifdef NOTYET
2392  { "log", '\0', POPT_ARG_MAINCALL, cmd_log, ARGMINMAX(0,0),
2393  N_("Show commit logs."), NULL },
2394  { "merge", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2395  N_("Join two or more development histories together."), NULL },
2396  { "mv", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2397  N_("Move or rename a file, a directory, or a symlink."), NULL },
2398  { "pull", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2399  N_("Fetch from and merge with another repository or a local branch."), NULL },
2400  { "push", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2401  N_("Update remote refs along with associated objects."), NULL },
2402  { "rebase", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2403  N_("Forward-port local commits to the updated upstream head."), NULL },
2404  { "reset", '\0', POPT_ARG_MAINCALL, cmd_reset, ARGMINMAX(0,0),
2405  N_("Reset current HEAD to the specified state."), NULL },
2406  { "rm", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2407  N_("Remove files from the working tree and from the index."), NULL },
2408  { "show", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2409  N_("Show various types of objects."), NULL },
2410 #endif /* NOTYET */
2411  { "status", '\0', POPT_ARG_MAINCALL, rpmgitCmdStatus, ARGMINMAX(0,0),
2412  N_("Show the working tree status."), NULL },
2413 #ifdef NOTYET
2414  { "tag", '\0', POPT_ARG_MAINCALL, cmd_tag, ARGMINMAX(0,0),
2415  N_("Create, list, delete or verify a tag object signed with GPG."), NULL },
2416 #endif /* NOTYET */
2417 
2418 /* --- PLUMBING: manipulation */
2419 #ifdef NOTYET
2420  { "apply", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2421  N_("Apply a patch to files and/or to the index."), NULL },
2422  { "checkout-index", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2423  N_("Copy files from the index to the working tree."), NULL },
2424  { "commit-tree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2425  N_("Create a new commit object."), NULL },
2426  { "hash-object", '\0', POPT_ARG_MAINCALL, cmd_hash_object, ARGMINMAX(0,0),
2427  N_("Compute object ID and optionally creates a blob from a file."), NULL },
2428  { "index-pack", '\0', POPT_ARG_MAINCALL, cmd_index_pack, ARGMINMAX(0,0),
2429  N_("Build pack index file for an existing packed archive."), NULL },
2430  { "merge-file", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2431  N_("Run a three-way file merge."), NULL },
2432  { "merge-index", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2433  N_("Run a merge for files needing merging."), NULL },
2434  { "mktag", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2435  N_("Creates a tag object."), NULL },
2436  { "mktree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2437  N_("Build a tree-object from ls-tree formatted text."), NULL },
2438  { "pack-objects", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2439  N_("Create a packed archive of objects."), NULL },
2440  { "prune-packed", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2441  N_("Remove extra objects that are already in pack files."), NULL },
2442  { "read-tree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2443  N_("iReads tree information into the index."), NULL },
2444  { "symbolic-ref", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2445  N_("iRead and modify symbolic refs."), NULL },
2446  { "unpack-objects", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2447  N_("Unpack objects from a packed archive."), NULL },
2448  { "update-index", '\0', POPT_ARG_MAINCALL, cmd_update_index, ARGMINMAX(0,0),
2449  N_("Register file contents in the working tree to the index."), NULL },
2450  { "update-ref", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2451  N_("Update the object name stored in a ref safely."), NULL },
2452  { "write-tree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2453  N_("Create a tree object from the current index."), NULL },
2454 #endif /* NOTYET */
2455 
2456 /* --- PLUMBING: interrogation */
2457 #ifdef NOTYET
2458  { "archive", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2459  N_("Create a tar/zip archive of the files in the named tree object."), NULL },
2460  { "cat-file", '\0', POPT_ARG_MAINCALL, cmd_cat_file, ARGMINMAX(0,0),
2461  N_("Provide content or type and size information for repository objects."), NULL },
2462  { "diff-files", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2463  N_("Compares files in the working tree and the index."), NULL },
2464  { "diff-index", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2465  N_("Compares content and mode of blobs between the index and repository."), NULL },
2466  { "diff-tree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2467  N_("Compares the content and mode of blobs found via two tree objects."), NULL },
2468  { "for-each-ref", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2469  N_("Output information on each ref."), NULL },
2470  { "ls-files", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2471  N_("Show information about files in the index and the working tree."), NULL },
2472  { "ls-remote", '\0', POPT_ARG_MAINCALL, cmd_ls_remote, ARGMINMAX(0,0),
2473  N_("List references in a remote repository."), NULL },
2474  { "ls-tree", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2475  N_("List the contents of a tree object."), NULL },
2476  { "merge-base", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2477  N_("Find as good common ancestors as possible for a merge."), NULL },
2478  { "name-rev", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2479  N_("Find symbolic names for given revs."), NULL },
2480  { "pack-redundant", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2481  N_("Find redundant pack files."), NULL },
2482  { "rev-list", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2483  N_("Lists commit objects in reverse chronological order."), NULL },
2484  { "show-index", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2485  N_("Show packed archive index."), NULL },
2486  { "show-ref", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2487  N_("List references in a local repository."), NULL },
2488  { "unpack-file", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2489  N_("Creates a temporary file with blob contents."), NULL },
2490  { "var", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2491  N_("Show a git logical variable."), NULL },
2492  { "verify-pack", '\0', POPT_ARG_MAINCALL, cmd_noop, ARGMINMAX(0,0),
2493  N_("Validate packed git archive files."), NULL },
2494 #endif /* NOTYET */
2495 
2496 /* --- WIP */
2497 #ifdef NOTYET
2498  { "config", '\0', POPT_ARG_MAINCALL, cmd_config, ARGMINMAX(0,0),
2499  N_("Show git configuration."), NULL },
2500  { "index", '\0', POPT_ARG_MAINCALL, cmd_index, ARGMINMAX(0,0),
2501  N_("Show git index."), NULL },
2502  { "refs", '\0', POPT_ARG_MAINCALL, cmd_refs, ARGMINMAX(0,0),
2503  N_("Show git references."), NULL },
2504  { "rev-parse", '\0', POPT_ARG_MAINCALL,cmd_rev_parse, ARGMINMAX(0,0),
2505  N_("."), NULL },
2506 
2507  { "index-pack-old", '\0', POPT_ARG_MAINCALL, cmd_index_pack_old, ARGMINMAX(0,0),
2508  N_("Index a <PACKFILE>."), N_("<PACKFILE>") },
2509 #endif /* NOTYET */
2510 
2511  POPT_TABLEEND
2512 };
2513 
2514 #ifdef NOTYET
2515 static rpmRC rpmgitCmdHelp(int argc, /*@unused@*/ char *argv[])
2516 {
2517  FILE * fp = stdout;
2518  struct poptOption * c;
2519 
2520  fprintf(fp, "Commands:\n\n");
2521  for (c = _rpmgitCommandTable; c->longName != NULL; c++) {
2522  fprintf(fp, " %s %s\n %s\n\n",
2523  c->longName, (c->argDescrip ? c->argDescrip : ""), c->descrip);
2524  }
2525  return RPMRC_OK;
2526 }
2527 
2528 static rpmRC rpmgitCmdRun(int argc, /*@unused@*/ char *argv[])
2529 {
2530  struct poptOption * c;
2531  const char * cmd;
2532  rpmRC rc = RPMRC_FAIL;
2533 
2534  if (argv == NULL || argv[0] == NULL) /* XXX segfault avoidance */
2535  goto exit;
2536  cmd = argv[0];
2537  for (c = _rpmgitCommandTable; c->longName != NULL; c++) {
2538  rpmRC (*func) (int argc, char *argv[]) = NULL;
2539 
2540  if (strcmp(cmd, c->longName))
2541  continue;
2542 
2543  func = c->arg;
2544  rc = (*func) (argc, argv);
2545  break;
2546  }
2547 
2548 exit:
2549  return rc;
2550 }
2551 #endif /* NOTYET */
2552 
2553 /*==============================================================*/
2554 
2555 /* XXX move to struct rpmgit_s? */
2556 static const char * exec_path;
2557 static const char * html_path;
2558 static const char * man_path;
2559 static const char * info_path;
2560 static int paginate;
2562 static int bare;
2563 
2564 static struct poptOption rpmgitOpts[] = {
2565  /* XXX --version */
2566  { "exec-path", '\0', POPT_ARG_STRING, &exec_path, 0,
2567  N_("Set exec path to <DIR>. env(GIT_EXEC_PATH)"), N_("<DIR>") },
2568  { "html-path", '\0', POPT_ARG_STRING, &html_path, 0,
2569  N_("Set html path to <DIR>. env(GIT_HTML_PATH)"), N_("<DIR>") },
2570  { "man-path", '\0', POPT_ARG_STRING, &man_path, 0,
2571  N_("Set man path to <DIR>."), N_("<DIR>") },
2572  { "info-path", '\0', POPT_ARG_STRING, &info_path, 0,
2573  N_("Set info path to <DIR>."), N_("<DIR>") },
2574  /* XXX --pager */
2575  { "paginate", 'p', POPT_ARG_VAL, &paginate, 1,
2576  N_("Set paginate. env(PAGER)"), NULL },
2577  { "no-paginate", '\0', POPT_ARG_VAL, &paginate, 0,
2578  N_("Set paginate. env(PAGER)"), NULL },
2579  { "no-replace-objects", '\0', POPT_ARG_VAL, &no_replace_objects, 1,
2580  N_("Do not use replacement refs for objects."), NULL },
2581  { "bare", '\0', POPT_ARG_VAL, &bare, 1,
2582  N_("Treat as a bare repository."), NULL },
2583 
2584  { "git-dir", '\0', POPT_ARG_STRING, &_rpmgit_dir, 0,
2585  N_("Set git repository dir to <DIR>. env(GIT_DIR)"), N_("<DIR>") },
2586  { "work-tree", '\0', POPT_ARG_STRING, &_rpmgit_tree, 0,
2587  N_("Set git work tree to <DIR>. env(GIT_WORK_TREE)"), N_("<DIR>") },
2588 
2589  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmgitCmds, 0,
2590  N_("The most commonly used git commands are::"),
2591  NULL },
2592 
2593 #ifdef NOTYET
2594  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
2595  N_("Common options for all rpmio executables:"),
2596  NULL },
2597 
2598  POPT_AUTOALIAS
2599 #endif
2600  POPT_AUTOHELP
2601 
2602  { NULL, (char) -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
2603  N_("\
2604 usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n\
2605  [-p|--paginate|--no-pager] [--no-replace-objects]\n\
2606  [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n\
2607  [--help] COMMAND [ARGS]\n\
2608 \n\
2609 The most commonly used git commands are:\n\
2610  add Add file contents to the index\n\
2611  bisect Find by binary search the change that introduced a bug\n\
2612  branch List, create, or delete branches\n\
2613  checkout Checkout a branch or paths to the working tree\n\
2614  clone Clone a repository into a new directory\n\
2615  commit Record changes to the repository\n\
2616  diff Show changes between commits, commit and working tree, etc\n\
2617  fetch Download objects and refs from another repository\n\
2618  grep Print lines matching a pattern\n\
2619  init Create an empty git repository or reinitialize an existing one\n\
2620  log Show commit logs\n\
2621  merge Join two or more development histories together\n\
2622  mv Move or rename a file, a directory, or a symlink\n\
2623  pull Fetch from and merge with another repository or a local branch\n\
2624  push Update remote refs along with associated objects\n\
2625  rebase Forward-port local commits to the updated upstream head\n\
2626  reset Reset current HEAD to the specified state\n\
2627  rm Remove files from the working tree and from the index\n\
2628  show Show various types of objects\n\
2629  status Show the working tree status\n\
2630  tag Create, list, delete or verify a tag object signed with GPG\n\
2631 \n\
2632 See 'git COMMAND --help' for more information on a specific command.\n\
2633 "), NULL},
2634 
2635  POPT_TABLEEND
2636 };
2637 
2638 /*==============================================================*/
2639 
2640 static void rpmgitFini(void * _git)
2641  /*@globals fileSystem @*/
2642  /*@modifies *_git, fileSystem @*/
2643 {
2644  rpmgit git = (rpmgit) _git;
2645 
2646 #if defined(WITH_LIBGIT2)
2647  if (git->walk)
2648  git_revwalk_free(git->walk);
2649  if (git->odb)
2650  git_odb_free(git->odb);
2651  if (git->cfg)
2652  git_config_free(git->cfg);
2653  if (git->S)
2654  git_signature_free(git->S);
2655  if (git->H)
2656  git_commit_free(git->H);
2657  if (git->C)
2658  git_commit_free(git->C);
2659  if (git->T)
2660  git_tree_free(git->T);
2661  if (git->I)
2662  git_index_free(git->I);
2663  if (git->R)
2664  git_repository_free(git->R);
2665 
2666  /* XXX elsewhere: assumes rpmgitNew()/rpmgitFree() calls pair */
2667  if (--_rpmgit_threads <= 0) {
2668  git_threads_shutdown();
2669  _rpmgit_threads = 0;
2670  }
2671 #endif
2672  git->walk = NULL;
2673  git->odb = NULL;
2674  git->cfg = NULL;
2675  git->S = NULL;
2676  git->H = NULL;
2677  git->C = NULL;
2678  git->T = NULL;
2679  git->I = NULL;
2680  git->R = NULL;
2681 
2682  git->fp = NULL;
2683  git->data = NULL;
2684 
2685  git->hide = 0;
2686  git->sorting = 0;
2687  git->state = 0;
2688  git->shared_umask = 0;
2689 
2690  git->rev = 0;
2691  git->minor = 0;
2692  git->major = 0;
2693 
2694  git->user_email = _free(git->user_email);
2695  git->user_name = _free(git->user_name);
2696 
2697  git->core_repositoryformatversion = 0;
2698  git->core_bare = 0;
2699  git->is_bare = 0;
2700 
2701  git->workdir = _free(git->workdir);
2702  git->repodir = _free(git->repodir);
2703 
2704  git->ac = 0;
2705  git->av = argvFree(git->av);
2706  git->con = poptFreeContext(git->con);
2707 
2708  git->flags = 0;
2709  git->fn = _free(git->fn);
2710 
2711 }
2712 
2713 /*@unchecked@*/ /*@only@*/ /*@null@*/
2715 
2716 /*@unchecked@*/ /*@relnull@*/
2718 
2720  /*@globals _rpmgitPool, fileSystem @*/
2721  /*@modifies pool, _rpmgitPool, fileSystem @*/
2722 {
2723  rpmgit git;
2724 
2725  if (_rpmgitPool == NULL) {
2726  _rpmgitPool = rpmioNewPool("git", sizeof(*git), -1, _rpmgit_debug,
2727  NULL, NULL, rpmgitFini);
2728  pool = _rpmgitPool;
2729  }
2730  git = (rpmgit) rpmioGetPool(pool, sizeof(*git));
2731  memset(((char *)git)+sizeof(git->_item), 0, sizeof(*git)-sizeof(git->_item));
2732  return git;
2733 }
2734 
2735 #ifdef NOTYET /* XXX rpmgitRun() uses pre-parsed git->av */
2736 /*@unchecked@*/
2737 static const char * _gitI_init = "\
2738 ";
2739 #endif /* NOTYET */
2740 
2741 static rpmgit rpmgitI(void)
2742  /*@globals _rpmgitI @*/
2743  /*@modifies _rpmgitI @*/
2744 {
2745  if (_rpmgitI == NULL)
2746  _rpmgitI = rpmgitNew(NULL, 0, NULL);
2747  return _rpmgitI;
2748 }
2749 
2750 rpmgit rpmgitNew(char ** av, uint32_t flags, void * _opts)
2751 {
2752  static char * _av[] = { (char *) "rpmgit", NULL };
2753  int initialize = (!(flags & 0x80000000) || _rpmgitI == NULL);
2754  rpmgit git = (flags & 0x80000000)
2755  ? rpmgitI() : rpmgitGetPool(_rpmgitPool);
2756  int ac;
2757 poptOption opts = (poptOption) _opts;
2758 const char * fn = _rpmgit_dir;
2759 int xx;
2760 
2761 if (_rpmgit_debug)
2762 fprintf(stderr, "==> %s(%p, 0x%x) git %p fn %s\n", __FUNCTION__, av, flags, git, fn);
2763 
2764  if (av == NULL) av = _av;
2765  if (opts == NULL) opts = rpmgitOpts;
2766 
2767  ac = argvCount((ARGV_t)av);
2768  if (ac > 1)
2769  xx = rpmgitPopt(git, ac, av, opts);
2770 
2771  git->fn = (fn ? xstrdup(fn) : NULL);
2772  git->flags = flags;
2773 
2774 #if defined(WITH_LIBGIT2)
2775 
2776  /* XXX elsewhere: assumes rpmgitNew() calls pair */
2777  if (_rpmgit_threads <= 0) {
2778  git_threads_init();
2779  _rpmgit_threads = 1;
2780  }
2781 
2782  if (initialize) {
2783  struct stat sb;
2784  int xx;
2785  git_libgit2_version(&git->major, &git->minor, &git->rev);
2786 #ifdef DYING
2787  if (git->fn && git->R == NULL) {
2788  git->repodir = xstrdup(git->fn);
2789  xx = chkgit(git, "git_repository_open",
2790  git_repository_open((git_repository **)&git->R, git->repodir));
2791  }
2792 #else
2793  if (git->fn && Stat(git->fn, &sb) == 0)
2794  xx = rpmgitOpen(git, git->fn);
2795 #if 0
2796 assert(xx == 0 && git->R != NULL && git->repodir != NULL);
2797 #endif
2798 #endif
2799  }
2800 
2801 #ifdef NOTYET /* XXX rpmgitRun() uses pre-parsed git->av */
2802  if (initialize) {
2803  static const char _rpmgitI_init[] = "%{?_rpmgitI_init}";
2804  const char * s = rpmExpand(_rpmgitI_init, _gitI_init, NULL);
2805  ARGV_t sav = NULL;
2806  const char * arg;
2807  int i;
2808 
2809  xx = argvSplit(&sav, s, "\n");
2810  for (i = 0; (arg = sav[i]) != NULL; i++) {
2811  if (*arg == '\0')
2812  continue;
2813  (void) rpmgitRun(git, arg, NULL);
2814  }
2815  sav = argvFree(sav);
2816  s = _free(s);
2817  }
2818 #endif /* NOTYET */
2819 #endif /* defined(WITH_LIBGIT2) */
2820 
2821  return rpmgitLink(git);
2822 }
2823 
2824 /* XXX str argument isn't used. */
2825 rpmRC rpmgitRun(rpmgit git, const char * str, const char ** resultp)
2826 {
2827  rpmRC rc = RPMRC_FAIL;
2828  const char * cmd;
2829  struct poptOption * c;
2830  rpmRC (*handler) (int argc, char *argv[]);
2831  int minargs;
2832  int maxargs;
2833 
2834 if (_rpmgit_debug)
2835 fprintf(stderr, "==> %s(%p,%s,%p)\n", __FUNCTION__, git, str, resultp);
2836 
2837  if (git == NULL) git = rpmgitI();
2838 
2839 
2840  if (git->av == NULL || git->av[0] == NULL) /* XXX segfault avoidance */
2841  goto exit;
2842  cmd = git->av[0];
2843  for (c = rpmgitCmds; c->longName != NULL; c++) {
2844  if (strcmp(cmd, c->longName))
2845  continue;
2846  handler = c->arg;
2847  minargs = (c->val >> 8) & 0xff;
2848  maxargs = (c->val ) & 0xff;
2849  break;
2850  }
2851  if (c->longName == NULL) {
2852  fprintf(stderr, "Unknown command '%s'\n", cmd);
2853  rc = RPMRC_FAIL;
2854  } else
2855  if (minargs && git->ac < minargs) {
2856  fprintf(stderr, "Not enough arguments for \"git %s\"\n", c->longName);
2857  rc = RPMRC_FAIL;
2858  } else
2859  if (maxargs && git->ac > maxargs) {
2860  fprintf(stderr, "Too many arguments for \"git %s\"\n", c->longName);
2861  rc = RPMRC_FAIL;
2862  } else {
2863  ARGV_t av = git->av;
2864  int ac = git->ac;
2865 
2866  git->av = NULL;
2867  git->ac = 0;
2868  rc = (*handler)(git->ac, (char **)git->av);
2869  git->av = av;
2870  git->ac = ac;
2871  }
2872 
2873 exit:
2874  return rc;
2875 }
const bson * b
Definition: bson.h:280
const char const double d
Definition: bson.h:800
#define SPEW(_t, _rc, _git)
Definition: rpmgit.c:30
int _rpmgit_debug
Definition: rpmgit.c:21
#define ARGMINMAX(_min, _max)
Definition: rpmgit.c:2362
void rpmgitPrintOid(const char *msg, const void *_oidp, void *_fp)
static const int zero
Definition: bson.c:111
const char const char * cmd
Definition: mongo.h:777
struct rpmgit_s * rpmgit
Definition: rpmgit.h:10
char * xstrdup(const char *str)
Definition: rpmmalloc.c:321
Definition: libsql.c:29
const char * _rpmgit_dir
Definition: rpmgit.c:23
const bson * obj
Definition: bson.h:269
static const int ZERO
Definition: mongo.c:2020
char * gidToGname(gid_t gid)
Definition: ugid.c:171
int argvAppend(ARGV_t *argvp, ARGV_t av)
Append one argv array to another.
Definition: argv.c:216
int Stat(const char *path, struct stat *st)
stat(2) clone.
Definition: rpmrpc.c:1361
rpmRC rpmgitCmdDiff(int argc, char *argv[])
Definition: rpmgit.c:1653
int Open(const char *path, int flags, mode_t mode)
open(2) clone.
Definition: rpmrpc.c:219
static int no_replace_objects
Definition: rpmgit.c:2561
rpmgit rpmgitLink(rpmgit git)
Reference a git wrapper instance.
int rpmgitTree(rpmgit git)
Definition: rpmgit.c:730
static int rpmgitPopt(rpmgit git, int argc, char *argv[], poptOption opts)
Definition: rpmgit.c:900
void rpmgitPrintSig(const char *msg, const void *___S, void *_fp)
rpmioPool _rpmgitPool
Definition: rpmgit.c:2714
static void rpmlog(int code, const char *fmt,...)
Definition: rpmlog.h:299
void rpmgitPrintCommit(rpmgit git, void *___C, void *_fp)
rpmgit _rpmgitI
Definition: rpmgit.c:2717
static void rpmgitFini(void *_git)
Definition: rpmgit.c:2640
static struct poptOption rpmgitCmds[]
Definition: rpmgit.c:2364
void rpmgitPrintTag(rpmgit git, void *_tag, void *_fp)
void rpmgitPrintRepo(rpmgit git, void *___R, void *_fp)
static int bare
Definition: rpmgit.c:2562
char * alloca()
static const char * fmtBits(uint32_t flags, KEY tbl[], size_t ntbl, char *t)
Definition: db3.c:209
rpmRC rpmgitCmdAdd(int argc, char *argv[])
Definition: rpmgit.c:1300
const char * str
Definition: bson.h:593
Yet Another syslog(3) API clone.
void rpmgitPrintTree(void *___T, void *_fp)
const char const bson_bool_t v
Definition: bson.h:919
int rpmgitAddFile(rpmgit git, const char *fn)
Definition: rpmgit.c:505
rpmgit rpmgitFree(rpmgit git)
Destroy a git wrapper.
rpmioItem rpmioGetPool(rpmioPool pool, size_t size)
Get unused item from pool, or alloc a new item.
Definition: rpmmalloc.c:220
static const char * tblName(uint32_t v, KEY *tbl, size_t ntbl)
Definition: db3.c:189
#define N_(Text)
Definition: system.h:531
int argvCount(const ARGV_t argv)
Return no.
Definition: argv.c:71
int rpmgitWalk(rpmgit git)
Definition: rpmgit.c:744
mongo_error_t err
Definition: mongo.h:922
const char const bson_oid_t * oid
Definition: bson.h:757
const char const bson * command
Definition: mongo.h:762
static const char * exec_path
Definition: rpmgit.c:2556
const char const bson * data
Definition: mongo.h:463
const char const bson const bson int limit
Definition: mongo.h:569
int rpmgitConfig(rpmgit git)
Definition: rpmgit.c:653
ARGV_t argvFree(ARGV_t argv)
Destroy an argv array.
Definition: argv.c:44
uint32_t v
Definition: libsql.c:31
static const char * file
Definition: parseFiles.c:20
static enum FN_e filenames
Definition: rpmgrep.c:134
void rpmgitPrintIndex(void *___I, void *_fp)
static rpmgit rpmgitI(void)
Definition: rpmgit.c:2741
const char const char * user
Definition: mongo.h:828
int rpmgitClose(rpmgit git)
Definition: rpmgit.c:688
#define _ENTRY(_v)
Definition: db3.c:236
static rpmgit rpmgitGetPool(rpmioPool pool)
Definition: rpmgit.c:2719
static const char * html_path
Definition: rpmgit.c:2557
char * rpmExpand(const char *arg,...)
Return (malloc'ed) concatenated macro expansion(s).
Definition: macro.c:3238
const char const char int arg
Definition: mongo.h:777
static struct poptOption rpmgitOpts[]
Definition: rpmgit.c:2564
const char const bson int mongo_write_concern int flags
Definition: mongo.h:485
int rpmgitWrite(rpmgit git)
Definition: rpmgit.c:877
void argvPrint(const char *msg, ARGV_t argv, FILE *fp)
Print argv array elements.
Definition: argv.c:19
rpmRC rpmgitCmdInit(int argc, char *argv[])
Definition: rpmgit.c:1045
static void output(int indent, int *offset, const char *fmt,...)
Definition: rpmmtree.c:2497
int rpmgitOpen(rpmgit git, const char *repodir)
Definition: rpmgit.c:705
enum rpmRC_e rpmRC
RPM return codes.
const char const char const char * opts
Definition: bson.h:971
const char const int i
Definition: bson.h:778
rpmRC rpmgitCmdCommit(int argc, char *argv[])
Definition: rpmgit.c:1391
static int paginate
Definition: rpmgit.c:2560
static int snprintf(char *buf, int nb, const char *fmt,...)
Definition: rpmps.c:220
int rpmgitCommit(rpmgit git, const char *msg)
Definition: rpmgit.c:535
int rpmgitInit(rpmgit git, void *initopts)
Definition: rpmgit.c:473
rpmioPool rpmioNewPool(const char *name, size_t size, int limit, int flags, char *(*dbg)(void *item), void(*init)(void *item), void(*fini)(void *item))
Create a memory pool.
Definition: rpmmalloc.c:109
char * stpcpy(char *dest, const char *src)
static int _rpmgit_threads
Definition: rpmgit.c:28
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:756
char * uidToUname(uid_t uid)
Definition: ugid.c:135
const bson * in
Definition: bson.h:746
rpmRC rpmgitRun(rpmgit git, const char *str, const char **resultp)
Execute git string.
Definition: rpmgit.c:2825
int argvSplit(ARGV_t *argvp, const char *str, const char *seps)
Split a string into an argv array.
Definition: argv.c:233
int rpmgitInfo(rpmgit git)
Definition: rpmgit.c:789
rpmRC rpmgitCmdStatus(int argc, char *argv[])
Definition: rpmgit.c:2214
static const char * info_path
Definition: rpmgit.c:2559
const char * _rpmgit_tree
Definition: rpmgit.c:25
static const char * name
struct key_s KEY
ARGstr_t * ARGV_t
Definition: argv.h:12
struct poptOption rpmioAllPoptTable[]
Popt option table for options shared by all modes and executables.
Definition: poptIO.c:564
static cptr_t current[2]
Definition: rpmrc.c:128
rpmgit rpmgitNew(char **av, uint32_t flags, void *_opts)
Create and load a git wrapper.
Definition: rpmgit.c:2750
static const char * man_path
Definition: rpmgit.c:2558
const char * n
Definition: db3.c:184
const char const bson const bson int int int options
Definition: mongo.h:569
char * Realpath(const char *path, char *resolved_path)
realpath(3) clone.
Definition: rpmrpc.c:2330
void rpmgitPrintHead(rpmgit git, void *___H, void *_fp)
void rpmgitPrintTime(const char *msg, time_t _Ctime, void *_fp)
const char * rpmgitOid(rpmgit git, const void *_oid)
Definition: rpmgit.c:675
int rpmgitRead(rpmgit git)
Definition: rpmgit.c:850
const char * ns
Definition: mongo.h:326