Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Uses git to get version number.
5"""
7import os
8import sys
9import datetime
10import warnings
11import xml.etree.ElementTree as ET
12import re
13from xml.sax.saxutils import escape
15from ..flog import fLOG, run_cmd
18class GitException(Exception):
19 """
20 Exception raised by this module.
21 """
22 pass
25def my_date_conversion(sdate):
26 """
27 Converts a date into a datetime.
29 @param sdate string
30 @return date
31 """
32 first = sdate.split(" ")[0]
33 trois = first.replace(".", "-").replace("/", "-").split("-")
34 return datetime.datetime(int(trois[0]), int(trois[1]), int(trois[2]))
37def IsRepo(location, commandline=True):
38 """
39 Says if it a repository :epkg:`GIT`.
41 @param location (str) location
42 @param commandline (bool) use commandline or not
43 @return bool
44 """
45 if location is None:
46 location = os.path.normpath(os.path.abspath(
47 os.path.join(os.path.split(__file__)[0], "..", "..", "..", "..")))
49 try:
50 get_repo_version(location, commandline, log=False)
51 return True
52 except Exception:
53 return False
56class RepoFile:
58 """
59 Mimic a :epkg:`GIT` file.
60 """
62 def __init__(self, **args):
63 """
64 @param args list of members to add
65 """
66 for k, v in args.items():
67 self.__dict__[k] = v
69 if hasattr(self, "name"):
70 if '"' in self.name: # pylint: disable=E0203
71 #defa = sys.stdout.encoding if sys.stdout != None else "utf8"
72 self.name = self.name.replace('"', "")
73 #self.name = self.name.encode(defa).decode("utf-8")
74 if "\\303" in self.name or "\\302" in self.name or "\\342" in self.name:
75 # don't know yet how to avoid that
76 name0 = self.name
77 # see http://www.utf8-chartable.de/unicode-utf8-table.pl?utf8=oct
78 # far from perfect
79 self.name = self.name.replace(r"\302\240", chr(160)) \
80 .replace(r"\302\246", "¦") \
81 .replace(r"\302\256", "®") \
82 .replace(r"\302\251", "©") \
83 .replace(r"\302\260", "°") \
84 .replace(r"\302\267", "·") \
85 .replace(r"\303\203", "Ã") \
86 .replace(r"\303\207", "Ç") \
87 .replace(r"\303\211", "e") \
88 .replace(r"\303\232", "Ú") \
89 .replace(r"\303\240", "à") \
90 .replace(r"\303\242", "â") \
91 .replace(r"\303\244", "ä") \
92 .replace(r"\303\246", "æ") \
93 .replace(r"\303\247", chr(231)) \
94 .replace(r"\303\250", chr(232)) \
95 .replace(r"\303\251", chr(233)) \
96 .replace(r"\303\252", "ê") \
97 .replace(r"\303\253", "ë") \
98 .replace(r"\303\256", "î") \
99 .replace(r"\303\257", "ï") \
100 .replace(r"\303\264", "ô") \
101 .replace(r"\303\266", "ö") \
102 .replace(r"\303\273", "û") \
103 .replace(r"\303\274", "ü") \
104 .replace(r"a\314\200", "à") \
105 .replace(r"e\314\201", "é") \
106 .replace(r"\342\200\231", "’")
107 if not os.path.exists(self.name):
108 try:
109 ex = os.path.exists(name0)
110 except ValueError as e:
111 ex = str(e)
112 warnings.warn(
113 "The modification did not work\n'{0}'\nINTO\n'{1}'\n[{2}\nexists: {3}]".format(
114 name0, self.name, [self.name], ex))
116 def __str__(self):
117 """
118 usual
119 """
120 return self.name
123def get_cmd_git():
124 """
125 Gets the command line used to run :epkg:`git`.
127 @return string
128 """
129 if sys.platform.startswith("win32"): # pragma: no cover
130 cmd = r'"C:\Program Files\Git\bin\git.exe"'
131 if not os.path.exists(cmd):
132 cmd = r'"C:\Program Files (x86)\Git\bin\git.exe"'
133 if not os.path.exists(cmd):
134 # hoping git path is included in environment variable PATH
135 cmd = "git"
136 else:
137 cmd = 'git'
138 return cmd
141def repo_ls(full, commandline=True):
142 """
143 Runs ``ls`` on a path.
145 @param full full path
146 @param commandline use command line instead of pysvn
147 @return output of client.ls
148 """
150 if not commandline: # pragma: no cover
151 try:
152 raise NotImplementedError()
153 except Exception:
154 return repo_ls(full, True)
155 else:
156 cmd = get_cmd_git()
157 cmd += " ls-tree -r HEAD \"%s\"" % full
158 out, err = run_cmd(cmd,
159 wait=True,
160 encerror="strict",
161 encoding=sys.stdout.encoding if sys.stdout is not None else "utf8",
162 change_path=os.path.split(
163 full)[0] if os.path.isfile(full) else full,
164 shell=sys.platform.startswith("win32"))
165 if len(err) > 0:
166 raise GitException( # pragma: no cover
167 "Issue with path '{0}'\n[OUT]\n{1}\n[ERR]\n{2}".format(full, out, err))
169 res = [RepoFile(name=os.path.join(full, _.strip().split("\t")[-1]))
170 for _ in out.split("\n") if len(_) > 0]
171 return res
174def __get_version_from_version_txt(path):
175 """
176 Private function, tries to find a file ``version.txt`` which should
177 contains the version number (if :epkg:`svn` is not present).
179 @param path folder to look, it will look to the the path of this file,
180 some parents directories and finally this path
181 @return the version number
183 @warning If ``version.txt`` was not found, it throws an exception.
184 """
185 file = os.path.split(__file__)[0]
186 paths = [file,
187 os.path.join(file, ".."),
188 os.path.join(file, "..", ".."),
189 os.path.join(file, "..", "..", ".."),
190 path]
191 for p in paths:
192 fp = os.path.join(p, "version.txt")
193 if os.path.exists(fp):
194 with open(fp, "r") as f:
195 return int(f.read().strip(" \n\r\t"))
196 raise FileNotFoundError(
197 "unable to find version.txt in\n" + "\n".join(paths))
200_reg_insertion = re.compile("([1-9][0-9]*) insertion")
201_reg_deletion = re.compile("([1-9][0-9]*) deletion")
202_reg_bytes = re.compile("([1-9][0-9]*) bytes")
205def get_file_details(name, path=None, commandline=True):
206 """
207 Returns information about a file.
209 @param name name of the file
210 @param path path to repo
211 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
212 @return list of tuples
214 The result is a list of tuple:
216 * commit
217 * name
218 * added
219 * inserted
220 * bytes
221 """
222 if not commandline: # pragma: no cover
223 try:
224 raise NotImplementedError()
225 except Exception:
226 return get_file_details(name, path, True)
227 else:
228 cmd = get_cmd_git()
229 if sys.platform.startswith("win"):
230 cmd += ' log --stat "' + os.path.join(path, name) + '"'
231 else:
232 cmd = [cmd, 'log', "--stat", os.path.join(path, name)]
234 enc = sys.stdout.encoding if sys.stdout is not None else "utf8"
235 out, err = run_cmd(cmd,
236 wait=True,
237 encerror="strict",
238 encoding=enc,
239 change_path=os.path.split(
240 path)[0] if os.path.isfile(path) else path,
241 shell=sys.platform.startswith("win32"),
242 preprocess=False)
244 if len(err) > 0: # pragma: no cover
245 mes = "Problem with file '{0}'".format(os.path.join(path, name))
246 raise GitException(
247 mes + "\n" +
248 err + "\nCMD:\n" + cmd + "\nOUT:\n" + out + "\n[giterror]\n" +
249 err + "\nCMD:\n" + cmd)
251 master = get_master_location(path, commandline)
252 if master.endswith(".git"):
253 master = master[:-4]
255 if enc != "utf8" and enc is not None:
256 by = out.encode(enc)
257 out = by.decode("utf8")
259 # We split into commits.
260 commits = []
261 current = []
262 for line in out.split("\n"):
263 if line.startswith("commit"):
264 if len(current) > 0:
265 commits.append("\n".join(current))
266 current = [line]
267 else:
268 current.append(line)
269 if len(current) > 0:
270 commits.append("\n".join(current))
272 # We analyze each commit.
273 rows = []
274 for commit in commits:
275 se = _reg_insertion.findall(commit)
276 if len(se) > 1:
277 raise Exception( # pragma: no cover
278 "A commit is wrong \n{0}".format(commit))
279 inser = int(se[0]) if len(se) == 1 else 0
280 de = _reg_deletion.findall(commit)
281 if len(de) > 1:
282 raise Exception( # pragma: no cover
283 "A commit is wrong \n{0}".format(commit))
284 delet = int(de[0]) if len(de) == 1 else 0
285 bi = _reg_bytes.findall(commit)
286 if len(bi) > 1:
287 raise Exception( # pragma: no cover
288 "A commit is wrong \n{0}".format(commit))
289 bite = int(bi[0]) if len(bi) == 1 else 0
290 com = commit.split("\n")[0].split()[1]
291 rows.append((com, name.strip(), inser, delet, bite))
292 return rows
295_reg_stat_net = re.compile("(.+) *[|] +([1-9][0-9]*)")
296_reg_stat_bytes = re.compile(
297 "(.+) *[|] Bin ([0-9]+) [-][>] ([0-9]+) bytes")
300def get_file_details_all(path=None, commandline=True):
301 """
302 Returns information about all files
304 @param path path to repo
305 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
306 @return list of tuples
308 The result is a list of tuple:
310 * commit
311 * name
312 * net
313 * bytes
314 """
315 if not commandline: # pragma: no cover
316 try:
317 raise NotImplementedError()
318 except Exception:
319 return get_file_details_all(path, True)
320 else:
321 cmd = get_cmd_git()
322 if sys.platform.startswith("win"):
323 cmd += ' --no-pager log --stat'
324 else:
325 cmd = [cmd, '--no-pager', 'log', "--stat"]
327 enc = sys.stdout.encoding if sys.stdout is not None else "utf8"
328 out, err = run_cmd(cmd,
329 wait=True,
330 encerror="strict",
331 encoding=enc,
332 change_path=os.path.split(
333 path)[0] if os.path.isfile(path) else path,
334 shell=sys.platform.startswith("win32"),
335 preprocess=False)
337 if len(err) > 0: # pragma: no cover
338 mes = "Problem with '{0}'".format(path)
339 raise GitException(
340 mes + "\n" +
341 err + "\nCMD:\n" + cmd + "\nOUT:\n" + out + "\n[giterror]\n" +
342 err + "\nCMD:\n" + cmd)
344 master = get_master_location(path, commandline)
345 if master.endswith(".git"):
346 master = master[:-4]
348 if enc != "utf8" and enc is not None:
349 by = out.encode(enc)
350 out = by.decode("utf8")
352 # We split into commits.
353 commits = []
354 current = []
355 for line in out.split("\n"):
356 if line.startswith("commit"):
357 if len(current) > 0:
358 commits.append("\n".join(current))
359 current = [line]
360 else:
361 current.append(line)
362 if len(current) > 0:
363 commits.append("\n".join(current))
365 # We analyze each commit.
366 rows = []
367 for commit in commits:
368 com = commit.split("\n")[0].split()[1]
369 lines = commit.split("\n")
370 for line in lines:
371 r1 = _reg_stat_net.search(line)
372 if r1:
373 name = r1.groups()[0].strip()
374 net = int(r1.groups()[1])
375 delta = 0
376 else:
377 net = 0
378 r2 = _reg_stat_bytes.search(line)
379 if r2:
380 name = r2.groups()[0].strip()
381 fr = int(r2.groups()[1])
382 to = int(r2.groups()[2])
383 delta = to - fr
384 else:
385 continue
386 rows.append((com, name, net, delta))
387 return rows
390def get_repo_log(path=None, file_detail=False, commandline=True, subset=None):
391 """
392 Gets the latest changes operated on a file in a folder or a subfolder.
394 @param path path to look
395 @param file_detail if True, add impacted files
396 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
397 @param subset only provide file details for a subset of files
398 @return list of changes, each change is a list of tuple (see below)
400 The return results is a list of tuple with the following fields:
402 - author
403 - commit hash [:6]
404 - date (datetime)
405 - comment$
406 - full commit hash
407 - link to commit (if the repository is http://...)
409 The function use a command line if an error occurred.
410 It uses the xml format:
412 ::
414 <logentry revision="161">
415 <author>xavier dupre</author>
416 <date>2013-03-23T15:02:50.311828Z</date>
417 <msg>pyquickhelper: first version</msg>
418 <hash>full commit hash</hash>
419 </logentry>
421 Add link:
423 ::
425 https://github.com/sdpython/pyquickhelper/commit/8d5351d1edd4a8997f358be39da80c72b06c2272
427 More: `git pretty format <http://opensource.apple.com/source/Git/Git-19/src/git-htmldocs/pretty-formats.txt>`_
428 See also `pretty format <https://www.kernel.org/pub/software/scm/git/docs/git-log.html#_pretty_formats>`_ (html).
429 To get details about one file and all the commit.
431 ::
433 git log --stat -- _unittests/ut_loghelper/data/sample_zip.zip
435 For some reason, the call to @see fn str2datetime seemed to cause exception such as::
437 File "<frozen importlib._bootstrap>", line 2212, in _find_and_load_unlocked
438 File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
439 File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
440 File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
441 File "<frozen importlib._bootstrap>", line 2224, in _find_and_load_unlocked
443 when it was used to generate documentation for others modules than *pyquickhelper*.
444 Not using this function helps. The cause still remains obscure.
445 """
446 if file_detail:
447 if subset is None:
448 res = get_file_details_all(path, commandline=commandline)
449 details = {}
450 for commit in res:
451 com = commit[0]
452 if com not in details:
453 details[com] = []
454 details[com].append(commit[1:])
455 else:
456 files = subset
457 details = {}
458 for i, name in enumerate(files):
459 res = get_file_details(name.name if isinstance(name, RepoFile) else name,
460 path, commandline=commandline)
461 for commit in res:
462 com = commit[0]
463 if com not in details:
464 details[com] = []
465 details[com].append(commit[1:])
466 logs = get_repo_log(path=path, file_detail=False,
467 commandline=commandline)
468 final = []
469 for log in logs:
470 com = log[4]
471 if com not in details:
472 continue
473 det = details[com]
474 for d in det:
475 final.append(tuple(log) + d)
476 return final
478 if path is None:
479 path = os.path.normpath(
480 os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", "..")))
482 if not commandline: # pragma: no cover
483 try:
484 raise NotImplementedError()
485 except Exception:
486 return get_repo_log(path, file_detail, True)
487 else:
488 cmd = get_cmd_git()
489 if sys.platform.startswith("win"): # pragma: no cover
490 cmd += ' log --pretty=format:"<logentry revision=\\"%h\\">' + \
491 '<author>%an</author><date>%ci</date><hash>%H</hash><msg>%s</msg></logentry>" ' + \
492 path
493 else:
494 cmd_tmp = '--pretty=format:<logentry revision="%h"><author>%an</author><date>%ci' + \
495 '</date><hash>%H</hash><msg>%s</msg></logentry>'
496 cmd = [cmd, 'log', cmd_tmp, path]
498 enc = sys.stdout.encoding if sys.stdout is not None else "utf8"
499 out, err = run_cmd(cmd, wait=True, encerror="strict", encoding=enc,
500 change_path=os.path.split(
501 path)[0] if os.path.isfile(path) else path,
502 shell=sys.platform.startswith("win32"), preprocess=False)
504 if len(err) > 0: # pragma: no cover
505 mes = "Problem with file '{0}'".format(path)
506 raise GitException(mes + "\n" +
507 err + "\nCMD:\n" + cmd + "\nOUT:\n" + out +
508 "\n[giterror]\n" + err + "\nCMD:\n" + cmd)
510 master = get_master_location(path, commandline)
511 if master.endswith(".git"):
512 master = master[:-4]
514 if enc != "utf8" and enc is not None:
515 by = out.encode(enc)
516 out = by.decode("utf8")
518 out = out.replace("\n\n", "\n")
519 out = "<xml>\n%s\n</xml>" % out
520 try:
521 root = ET.fromstring(out)
522 except ET.ParseError:
523 # it might be due to character such as << >>
524 lines = out.split("\n")
525 out = []
526 suffix = "</msg></logentry>"
527 for line in lines:
528 if line.endswith(suffix):
529 pos = line.find("<msg>")
530 if pos == -1:
531 out.append(line)
532 continue
533 begin = line[:pos + 5]
534 body = line[pos + 5:-len(suffix)]
535 msg = escape(body)
536 line = begin + msg + suffix
537 out.append(line)
538 out = "\n".join(out)
539 try:
540 root = ET.fromstring(out)
541 except ET.ParseError as eee: # pragma: no cover
542 raise GitException(
543 "Unable to parse:\n{0}".format(out)) from eee
545 res = []
546 for i in root.iter('logentry'):
547 revision = i.attrib['revision'].strip()
548 author = i.find("author").text.strip()
549 t = i.find("msg").text
550 hash = i.find("hash").text
551 msg = t.strip() if t is not None else "-"
552 sdate = i.find("date").text.strip()
553 dt = my_date_conversion(sdate.replace("T", " ").strip("Z "))
554 row = [author, revision, dt, msg, hash]
555 if master.startswith("http"):
556 row.append(master + "/commit/" + hash)
557 else:
558 row.append("{0}//{1}".format(master, hash))
559 res.append(row)
560 return res
563def get_repo_version(path=None, commandline=True, usedate=False, log=False):
564 """
565 Gets the latest check for a specific path or version number
566 based on the date (if *usedate* is True).
567 If *usedate* is False, it returns a mini hash (a string then).
569 @param path path to look
570 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
571 @param usedate if True, it uses the date to return a minor version number (1.1.thisone)
572 @param log if True, returns the output instead of a boolean
573 @return integer)
574 """
575 if not usedate:
576 last = get_nb_commits(path, commandline)
577 return last
578 else: # pragma: no cover
579 if path is None:
580 path = os.path.normpath(
581 os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", "..")))
583 if not commandline:
584 try:
585 raise NotImplementedError()
586 except Exception:
587 return get_repo_version(path, True)
588 else:
589 cmd = get_cmd_git()
590 cmd += ' git log --format="%h---%ci"'
592 if path is not None:
593 cmd += " \"%s\"" % path
595 try:
596 out, err = run_cmd(cmd, wait=True, encerror="strict",
597 encoding=sys.stdout.encoding if sys.stdout is not None else "utf8",
598 change_path=os.path.split(
599 path)[0] if os.path.isfile(path) else path,
600 log_error=False, shell=sys.platform.startswith("win32"))
601 except Exception as e:
602 raise GitException(
603 "Problem with subprocess. Path is '{0}'\n[OUT]\n{1}\n[ERR]\n{2}".format(path, out, err)) from e
605 if len(err) > 0:
606 if log:
607 fLOG("Problem with file ", path, err)
608 if log:
609 return "OUT\n{0}\n[giterror]{1}\nCMD:\n{2}".format(out, err, cmd)
610 else:
611 raise GitException(
612 "OUT\n{0}\n[giterror]{1}\nCMD:\n{2}".format(out, err, cmd))
614 lines = out.split("\n")
615 lines = [_.split("---") for _ in lines if len(_) > 0]
616 temp = lines[0]
617 if usedate:
618 dt = my_date_conversion(temp[1].replace("T", " ").strip("Z "))
619 dt0 = datetime.datetime(dt.year, 1, 1, 0, 0, 0)
620 res = "%d" % (dt - dt0).days
621 else:
622 res = temp[0]
624 if len(res) == 0:
625 raise GitException(
626 "The command 'git help' should return something.")
628 return res
631def get_master_location(path=None, commandline=True):
632 """
633 Gets the remote master location.
635 @param path path to look
636 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
637 @return integer (check in number)
638 """
639 if path is None:
640 path = os.path.normpath(
641 os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", "..")))
643 if not commandline: # pragma: no cover
644 try:
645 raise NotImplementedError()
646 except Exception:
647 return get_master_location(path, True)
648 else:
649 cmd = get_cmd_git()
650 cmd += " config --get remote.origin.url"
652 try:
653 out, err = run_cmd(cmd, wait=True, encerror="strict",
654 encoding=sys.stdout.encoding if sys.stdout is not None else "utf8",
655 change_path=os.path.split(
656 path)[0] if os.path.isfile(path) else path,
657 log_error=False, shell=sys.platform.startswith("win32"))
658 except Exception as e: # pragma: no cover
659 raise GitException(
660 "Problem with subprocess. Path is '{0}'\n[OUT]\n{1}\n[ERR]\n{2}".format(path, out, err)) from e
662 if len(err) > 0:
663 raise GitException( # pragma: no cover
664 "Problem with path '{0}'\n[OUT]\n{1}\n[ERR]\n{2}".format(path, out, err))
665 lines = out.split("\n")
666 lines = [_ for _ in lines if len(_) > 0]
667 res = lines[0]
669 if len(res) == 0:
670 raise GitException( # pragma: no cover
671 "The command 'git help' should return something.")
673 return res
676def get_nb_commits(path=None, commandline=True):
677 """
678 Returns the number of commit.
680 @param path path to look
681 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
682 @return integer
683 """
684 if path is None:
685 path = os.path.normpath(
686 os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", "..")))
688 if not commandline: # pragma: no cover
689 try:
690 raise NotImplementedError()
691 except Exception:
692 return get_nb_commits(path, True)
693 else:
694 cmd = get_cmd_git()
695 cmd += ' rev-list HEAD --count'
697 if path is not None:
698 cmd += " \"%s\"" % path
700 out, err = run_cmd(cmd,
701 wait=True,
702 encerror="strict",
703 encoding=sys.stdout.encoding if sys.stdout is not None else "utf8",
704 change_path=os.path.split(
705 path)[0] if os.path.isfile(path) else path,
706 log_error=False,
707 shell=sys.platform.startswith("win32"))
709 if len(err) > 0:
710 raise GitException( # pragma: no cover
711 "Unable to get commit number from path {0}\n[giterror]\n{1}\nCMD:\n{2}".format(path, err, cmd))
713 lines = out.strip()
714 try:
715 nb = int(lines)
716 except ValueError as e:
717 raise ValueError( # pragma: no cover
718 "unable to parse: " + lines + "\nCMD:\n" + cmd) from e
719 return nb
722def get_file_last_modification(path, commandline=True):
723 """
724 Returns the last modification of a file.
726 @param path path to look
727 @param commandline if True, use the command line to get the version number, otherwise it uses pysvn
728 @return integer
729 """
730 if path is None:
731 path = os.path.normpath(
732 os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", "..")))
734 if not commandline: # pragma: no cover
735 try:
736 raise NotImplementedError()
737 except Exception:
738 return get_file_last_modification(path, True)
739 else:
740 cmd = get_cmd_git()
741 cmd += ' log -1 --format="%ad" --'
742 cmd += " \"%s\"" % path
744 out, err = run_cmd(cmd,
745 wait=True,
746 encerror="strict",
747 encoding=sys.stdout.encoding if sys.stdout is not None else "utf8",
748 change_path=os.path.split(
749 path)[0] if os.path.isfile(path) else path,
750 log_error=False,
751 shell=sys.platform.startswith("win32"))
753 if len(err) > 0:
754 raise GitException( # pragma: no cover
755 "Unable to get commit number from path {0}\n[giterror]\n{1}\nCMD:\n{2}".format(path, err, cmd))
757 lines = out.strip("\n\r ")
758 return lines
761def clone(location, srv, group, project, username=None, password=None, fLOG=None):
762 """
763 Clones a :epkg:`git` repository.
765 @param location location of the clone
766 @param srv git server
767 @param group group
768 @param project project name
769 @param username username
770 @param password password
771 @param fLOG logging function
772 @return output, error
774 See `How to provide username and password when run "git clone git@remote.git"?
775 <http://stackoverflow.com/questions/10054318/how-to-provide-username-and-password-when-run-git-clone-gitremote-git>`_
777 .. exref::
778 :title: Clone a git repository
780 ::
782 clone("local_folder", "github.com", "sdpython", "pyquickhelper")
783 """
784 if username is not None:
785 address = "https://{0}:{1}@{2}/{3}/{4}.git".format(username,
786 password, srv, group, project)
787 else:
788 address = "https://{0}/{1}/{2}.git".format(srv, group, project)
790 cmd = get_cmd_git()
791 cmd += " clone " + address + " " + location
792 out, err = run_cmd(cmd, wait=True, fLOG=fLOG)
793 if len(err) > 0 and "Cloning into" not in err and "Clonage dans" not in err:
794 raise GitException( # pragma: no cover
795 "Unable to clone {0}\n[giterror]\n{1}\nCMD:\n{2}".format(address, err, cmd))
796 return out, err
799def rebase(location, srv, group, project, username=None, password=None, fLOG=None):
800 """
801 Runs ``git pull -rebase`` on a repository.
803 @param location location of the clone
804 @param srv git server
805 @param group group
806 @param project project name
807 @param username username
808 @param password password
809 @param fLOG logging function
810 @return output, error
811 """
812 if username is not None:
813 address = "https://{0}:{1}@{2}/{3}/{4}.git".format(username,
814 password, srv, group, project)
815 else:
816 address = "https://{0}/{1}/{2}.git".format(srv, group, project)
818 cwd = os.getcwd()
819 os.chdir(location)
820 cmd = get_cmd_git()
821 cmd += " pull --rebase " + address
822 out, err = run_cmd(cmd, wait=True, fLOG=fLOG)
823 os.chdir(cwd)
824 if len(err) > 0 and "-> FETCH_HEAD" not in err:
825 raise GitException( # pragma: no cover
826 "Unable to rebase {0}\n[giterror]\n{1}\nCMD:\n{2}".format(address, err, cmd))
827 return out, err