Учебник по GitPython¶

GitPython обеспечивает доступ объектной модели к вашему репозиторию git. Это руководство состоит из нескольких разделов, большинство из которых объясняет реальный вариант использования.

Весь представленный здесь код создан для обеспечения корректности. Знание этого также должно позволить вам более легко запускать код для ваших собственных целей тестирования, все, что вам нужно, это установка разработчика git-python.

Встречайте тип Repo¶

Первым шагом является создание объекта для представления вашего репозитория.

 из git import Repo # rorepo - это экземпляр Repo  указывающий на репозиторий git-python. # Насколько вам известно, первый аргумент Repo - это путь к репозиторию, # с которым вы хотите работать repo = Repo (self.rorepo.working_tree_dir) assert not repo.bare 

В приведенном выше примере каталог self.rorepo.working_tree_dir равен /Users/mtrier/Development/git-python и это мой рабочий репозиторий, содержащий каталог .git . Вы также можете инициализировать GitPython с помощью чистого репозитория.

 bare_repo = Repo.init (os.path.join (  rw_dir, 'bare-repo'), bare = True) assert bare_repo.bare 

Объект репо обеспечивает высокоуровневый доступ к вашим данным, он позволяет вам создавать и удалять заголовки, теги и пульты дистанционного управления и получать доступ к конфигурации репозитория.

 repo.config_reader () # получить программу чтения конфигурации для доступа только для чтения с  repo.config_writer (): # получить средство записи конфигурации для изменения прохода конфигурации # вызвать release (), чтобы убедиться, что изменения записаны и блокировки сняты 

Запросить активный ветки, запросить неотслеживаемые файлы или данные репозитория были изменены.

 assert not bare_repo.is_dirty () # проверить грязное состояниеrepo.untracked_files # получить  список неотслеживаемых файлов # ['my_untracked_file'] 

Клонировать из существующих репозиториев или инициализировать новые пустые.

 cloned_rep  o = repo.clone (os.path.join (rw_dir, 'to/this/path')) assert cloned_repo .__ class__ is Repo # клонировать существующий репозиторий assert Repo.init (os.path.join (rw_dir, 'path/for /new/repo')).__class__ is Repo 

Архивировать содержимое репозитория в файл tar.

 с open (os.path.join (rw_dir, 'repo.tar'), 'wb') как fp: repo.archive (fp) 

Расширенное использование репо¶

И, конечно же, вы можете сделать гораздо больше с этим типом, большая часть следующего будет объяснена более подробно в конкретных руководствах . Не волнуйтесь, если вы не сразу поняли некоторые из этих примеров, поскольку они могут потребовать глубокого понимания внутренней работы gits.

Запросить соответствующие пути к репозиторию…

 assert os.path.isdir (cloned_repo.working_tree_dir) # каталог с вашими рабочими файлами assert cloned_repo.git_dir.startswith (cloned_repo. working_tree_dir) # каталог, содержащий репозиторий gitassert bare_repo.working_tree_dir is None # у пустых репозиториев нет рабочего дерева 

В git-speak заголовки — это ветки. Ссылки — это указатели на конкретную фиксацию или на другие ссылки. Головы и являются своего рода справочниками. GitPython позволяет вам запрашивать их довольно интуитивно.

 self.assertEqual (repo.head.ref, repo.heads.master, # head - это сим-  ref, указывающий на мастер "Это нормально, если TC не запущен из` master`. ") self.assertEqual (repo.tags ['0.3.5'], repo.tag ('refs/tags/0.3.5')) # вы  также может получить доступ к тегам различными способами self.assertEqual (repo.refs.master, repo.heads ['master']) # .refs предоставляет все ссылки, то есть заголовки ... если 'TRAVIS' отсутствует в os.environ: self.assertEqual  (repo.refs ['origin/master'], repo.remotes.origin.refs.master) # ... удаленно ... self.assertEqual (repo.refs ['0.3.5'], repo.tags ['  0.3.5 ']) # ... и теги 

Вы также можете создавать новые головы…

 new_branch = cloned_repo.create_head ('feature') # создать новую ветку ... assert cloned_repo.active_branch! = new_branch # которая еще не была проверена ... self.assertEqual (new_branch.commit, cloned_repo  .active_branch.commit) # указывая на извлеченный коммит # Легко разрешить ветке указывать на предыдущий  commit, не влияя ни на что другое # Каждая ссылка обеспечивает доступ к объекту git, на который она указывает, обычно коммитирует assert new_branch.set_commit ('HEAD ~ 1'). commit == cloned_repo.active_branch.commit.parents [0] 

… и теги…

 past = cloned_repo.create_tag ('past', ref = new_branch, message  = "Это тег-объект, указывающий на% s"% new_branch.name) self.assertEqual (past.commit, new_branch.commit) # тег указывает на указанный commitassert past.tag.message.startswith ("This is"  ) # и его объект содержит сообщение providednow = cloned_repo.create_tag ('now') # Это ссылка на тег.  Он может не содержать метаданные assert now. Тег равен None 

Вы можете переходить вниз по ссылкам и другим объектам. Некоторые объекты, такие как коммиты , имеют дополнительные метаданные для запроса.

 assert now.commit.message! = Past.  commit.message # Вы можете читать объекты напрямую через двоичные потоки, никакого рабочего дерева не требуется assert (now.commit.tree/'VERSION'). data_stream.read (). decode ('ascii'). startwith ('3') # Вы  может также перемещаться по деревьям для обработки всех содержащихся файлов определенного коммитаfile_count = 0tree_count = 0tree = past.commit.tree for item в tree.traverse (): file_count + = item.type == 'blob' tree_count + = item.type =  = 'tree'assert file_count и tree_count # мы собрали все каталоги и файлыself.assertEqual (len (tree.blobs) + len (tree. tree), len (tree)) # дерево является итеративным для своих дочерних элементов 

позволяет обрабатывать операции выборки, извлечения и push, обеспечивая при этом необязательный прогресс в реальном времени информация для делегатов выполнения .

 из git import RemoteProgressclass MyProgressPrinter (RemoteProgress): def update (self, op_code, cur_count  , max_count = None, message = ''): print (op_code, cur_count, max_count, cur_count/(max_count или 100.0), message или «NO MESSAGE») # endself.assertEqual (len (cloned_repo.remotes), 1) # мы  были клонированы, поэтому должен быть один remoteself.assertEqual (len (bare_repo.remotes), 0) # этот был только что инициализированorigin = bare_repo.create_remote ('origin', url = cloned_repo.working_tree_dir) assert origin.exists () для fetch_info  in origin.fetch (progress = MyProgressPrinter ()): print ("Обновлено% s до% s"% (fetch_info.ref, fetch_info.commit)) # создайте локальную ветвь на последнем выбранном мастере.  Мы указываем имя статически, но у вас есть вся информация, # чтобы сделать это программно. Bare_master = bare_repo.create_head ('master', origin.refs.master) bare_repo.head.set_reference (bare_master) assert not bare_repo.delete_remote (origin  ) .exists () # push и pull ведут себя очень похоже 

В git-speak это также называется stage. Он используется для подготовки новых коммитов и может использоваться для хранения результатов операций слияния. Наша реализация индекса позволяет передавать дату в индекс, что полезно для пустых репозиториев, у которых нет рабочего дерева.

 self.assertEqual (new_branch.  checkout (), cloned_repo.active_branch) # ветка check out настраивает wtreeself.assertEqual (new_branch.commit, past.commit) # Теперь проверяется прошлоеnew_file_path = os.path.join (cloned_repo.working_tree_dir, 'my-new-file  ') open (new_file_path,' wb '). close () # создать новый файл в рабочем treecloned_repo.index.add ([new_file_path]) # добавить его в индекс # зафиксировать изменения для отклонения мастеров historycloned_repo.index.commit ("  В прошлом добавляли новый файл - для последующего использования ") # подготовить mergemaster = cloned_repo.heads.master # правая сторона впереди нас, в будущем merge_base = cloned_repo.merge_base (new_branch, master) # позволяет использовать три  -way mergecloned_repo.index.merge_tree (master, base = merge_base) # записать результат слияния в indexcloned_repo.index.commit ("Объединено прошлое и настоящее с будущим;)",  parent_commit = (new_branch.commit, master.commit)) # теперь new_branch опережает master, который, вероятно, следует проверить и мягко сбросить. # обратите внимание, что все эти операции не коснулись рабочего дерева, поскольку мы сами справились с этим.  # Это определенно требует, чтобы вы знали, что делаете :)! Assert os.path.basename (new_file_path) в new_branch.commit.tree # новый файл теперь находится в treemaster.commit = new_branch.commit # let master указывает на самый последний коммит cloned_repo  .голова. reference = master # мы изменили только ссылку, а не рабочее дерево или индекс 

Подмодули представляют все аспекты подмодулей git, который позволяет запрашивать всю связанную с ними информацию и манипулировать различными способами.

 # создать новый подмодуль и проверить его на месте, настроить  отслеживать основную ветку `bare_repo` # Поскольку в нашем репозитории GitPython уже есть подмодули, указывающие на GitHub, убедитесь, что мы # не взаимодействуем с ними для sm в cloned_repo.submodules: assert not sm.remove (). exists () # после удаления  , sm больше не существует sm = cloned_repo.create_submodule ('mysubrepo', 'path/to/subrepo', url = bare_repo.git_dir, branch = 'master') # .gitmodules был написан и добавлен в индекс, который является  cloned_repo.index.commit ("Добавлен подмодуль") assert sm.exists () и sm.module_exists () # этот подмодуль определенно доступен sm.remove (module = True, configuration = False) # удалить рабочее деревоassert sm.exists  (  ), а не sm.module_exists () # сам подмодуль все еще доступен # обновить все подмодули нерекурсивно, чтобы сэкономить время, этот метод очень мощный, попробуйте lookcloned_repo.submodule_update (recursive = False) assert sm.module_exists ()  # Рабочее дерево подмодулей было проверено обновлением 

Изменение ссылок¶

Вы можете легко создавать, удалять или изменять места, на которые они указывают.

 new_branch = repo.create_head ('new') # создать новую onenew_branch.  commit = 'HEAD ~ 10' # установить ветку для другого коммита без изменения индекса или рабочих деревьевrepo.delete_head (new_branch) # удалить существующий заголовок - работает, только если он не извлечен 

Создайте или удалите таким же образом, но вы не можете изменить их впоследствии.

 new_tag = repo.create_tag ('my_new_tag', message =  'my message') # Вы не можете изменить фиксацию, на которую указывает тег.  Теги необходимо создать зановоself.assertRaises (AttributeError, setattr, new_tag, 'commit', repo.commit ('HEAD ~ 1')) repo.delete_tag (new_tag) 

Измените, чтобы переключать ветви дешево (без настройки индекса или рабочего дерева).

 new_branch = repo.create_head ('another-branch  ') repo.head.reference = new_branch 

Понимание объектов¶

Объект все, что можно сохранить в объектной базе данных git. Объекты содержат информацию о своем типе, размере в несжатом виде, а также о фактических данных. Каждый объект однозначно идентифицируется двоичным хешем SHA1, имеющим размер 20 байтов или 40 байтов в шестнадцатеричной системе счисления.

Git знает только 4 различных типа объектов: Деревья и Теги .

В GitPython ко всем объектам можно обращаться через их общую базу, можно сравнивать и хешировать. Обычно они создаются не напрямую, а через ссылки или специализированные функции репозитория.

 hc = repo.head.commithct = hc.treehc! = Hct # @  NoEffecthc! = Repo.tags [0] # @NoEffecthc == repo.head.reference.commit # @NoEffect 

Общие поля …

 self.assertEqual (hct.type, 'tree') # предустановленный строковый тип, являющийся атрибутом класса assert hct.size> 0 # размер в байтах assert len ​​(hct.hexsha)  == 40assert len ​​(hct.binsha) == 20 

— это объекты, которые можно поместить в индекс git. Эти объекты представляют собой деревья, капли и подмодули, которые дополнительно знают свой путь в файловой системе, а также их режим.

 self.assertEqual (hct.path  , '') # в корневом дереве нет pathassert hct.trees [0] .path! = '' # у первого содержащегося элемента есть хотя бы self.assertEqual (hct.mode, 0o40000) # деревья имеют режим каталога linuxself.assertEqual  (hct.blobs [0] .mode, 0o100644) # BLOB-объекты имеют особый режим, сравнимый со стандартным Linux fs 

Доступ к данным (или любым данным объекта) с помощью потоки.

 hct.blobs [0] .data_stream.read () # объект потока для чтения данных из hct.blobs [0] .stream_data (open (  os.path.join (rw_dir, 'blob_data'), 'wb')) # записываем данные в указанный поток 

Объект Commit Commit

объекты содержат информацию о конкретном коммите. Получите фиксации, используя ссылки, как это сделано в разделе «Проверка ссылок» или как указано ниже.

Получить фиксации в указанной ревизии

 repo.commit  ('master') repo.commit ('v0.8.1') repo.commit ('HEAD ~ 10') 

Итерировать 50 коммитов, и если вам нужно разбиение на страницы, вы можете указать количество коммитов, которые нужно пропустить.

 пятьдесят_first_commit = list (repo.iter_commit ('master', max_count = 50)) assert len  (пятьдесят_first_commit) == 50 # это вернет коммиты 21-30 из списка коммитов как пройденные в обратном направлении masterten_commit_past_twenty = list (repo.iter_commit ('master', max_count = 10, skip = 20)) assert len ​​(ten_commits_past_twenty) == 10assert  пятьдесят_first_commits [20:30] == ten_commits_past_twenty 

Объект фиксации содержит все виды метаданных

 headcommit = repo.head.commitassert len ​​(headcommit.hexsha) == 40assert len ​​(headcommit.parents)> 0assert headcommit.tree.type == 'tree'assert len ​​(headcommit.author.name)! =  0 утверждать я  sinstance (headcommit.authored_date, int) assert len ​​(headcommit.committer.name)! = 0assert isinstance (headcommit.committed_date, int) assert headcommit.message! = '' 

Примечание. Дата и время представлены в формате секунд с начала эпохи . Преобразование в удобочитаемую форму можно выполнить с помощью различных методов.

 import timetime.asctime (time.gmtime (headcommit.committed_date)) time.strftime (  "% a,% d% b% Y% H:% M", время. gmtime (headcommit.committed_date)) 

Вы можете пройти по предкам фиксации, связав вызовы к parents

 assert headcommit.parents [0] .parents [0] .parents [0] == repo.commit ('master ^^^') 

Вышеупомянутое соответствует master ^^^ или master ~ 3 на языке git.

Объект Tree¶

A записывает указатели на содержимое каталога. Допустим, вам нужно корневое дерево последнего коммита в главной ветке

 tree = repo.heads.master.commit.treeassert len ​​(tree.hexsha  ) == 40 

Если у вас есть дерево, вы можете получить его содержимое

 assert len ​​(tree.trees)> 0 # деревья являются подкаталогами assert len ​​(tree.blobs)> 0 # blobs являются файлами assert len ​​(tree.blobs) + len (tree.trees) == len (tree) 

Полезно знать, что дерево ведет себя как список с возможностью запрашивать записи по имени

 self.assertEqual (tree ['smmap'], tree/'smmap') # доступ по индексу и подпутью для записи в дереве: # интуитивно понятная итерация элементов дерева print (entry) blob = tree.trees [1].  blobs [0] # давайте получим большой двоичный объект в поддереве assert blob.nameassert len ​​(blob.path)  

Существует удобный метод, который позволяет вам получить именованный подобъект из дерева с синтаксисом, аналогичным тому, как пути записываются в система posix

 assert tree/'smmap' == tree ['smmap'] assert tree/blob.path == tree [blob.path] 

Вы также можете получить корневое дерево коммита непосредственно из репозитория

 # Этот пример  показывает различные типы разрешенных ref-specsassert repo.tree () == repo.head.commit.treepast = repo.commit ('HEAD ~ 5') assert repo.tree (past) == repo.tree (past.hexsha  ) self.assertEqual (repo.tree ('v0.8.1'). type, 'tree') # да, вы можете указать любой refspec - работает везде 

Поскольку деревья разрешают прямой доступ только к своим промежуточным дочерним записям, используйте метод traverse, чтобы получить итератор для рекурсивного извлечения записей

 assert len ​​(tree)  

Примечание

Если деревья возвращают объекты подмодуля, они будут предполагать, что они существуют в момент фиксации текущего руководителя. Дерево, из которого он произошел, может быть укоренено в другой фиксации, о которой он не знает. Вот почему вызывающий должен установить принадлежность подмодуля или родительскую фиксацию с помощью метода set_parent_commit (my_commit) ..

Объект индекса¶

Индекс git — это этап, содержащий изменения, которые должны быть записаны при следующей фиксации или где, наконец, должно произойти слияние. Вы можете свободно обращаться к этой информации и управлять ею с помощью объекта. С легкостью измените индекс

 index = repo.index # Индекс содержит все капли в плоском  listassert len ​​(list (index.iter_blobs ())) == len ([o вместо o в repo.head.commit.tree.traverse () if o.type == 'blob']) # Доступ к объектам blob for (_path,  _stage), запись в index.entries.items (): passnew_file_path = os.path.join (repo.working_tree_dir, 'new-file-name') open (new_file_path, 'w'). close () index.add ([  new_file_path]) # добавить новый файл в indexindex.remove (['LICENSE']) # удалить существующий assert os.path.isfile (os.path.join (repo.working_tree_dir, 'LICENSE')) # рабочее дерево  untouchedself.assertEqual (index.commit ("мое сообщение о фиксации"). type, 'commit') # фиксация изменена indexrepo.active_branch.commit = repo.commit ('HEAD ~ 1') # забыть последнюю фиксацию из git import Actorauthor = Actor (  "Автор", "author@example.com") committer = Actor ("Коммиттер", "committer@example.com") # коммит по сообщению коммита и автору  и committerindex.commit ("мое сообщение фиксации", author = author, committer = committer) 

Создавать новые индексы из других деревьев или в результате слияния. Запишите этот результат в новый индексный файл для последующей проверки.

 из git import IndexFile # загружает дерево во временный индекс, который существует только в memoryIndexFile.  from_tree (repo, 'HEAD ~ 1') # трехкомпонентное слияние двух деревьев в memorymerge_index = IndexFile.from_tree (repo, 'HEAD ~ 10', 'HEAD', repo.merge_base ('HEAD ~ 10', 'HEAD')  ) # и сохраните его merge_index.write (os.path.join (rw_dir, 'merged_index')) 

Пульт управления

используются в качестве псевдонима для внешнего репозитория, чтобы облегчить отправку и выборку из них

 empty_repo = git.Repo.  init (os.path.join (rw_dir, 'empty')) origin = empty_repo.create_remote ('origin', repo.remotes.origin.url) assert origin.exists () assert origin == empty_repo.remotes.origin ==  empty_repo.remotes ['origin'] origin.fetch () # убедитесь, что у нас действительно есть данные.  fetch () возвращает полезную информацию # Настроить локальную ветвь отслеживания удаленного Branchempty_repo.create_head ('master', origin.refs.master) # создать локальную ветку "master" из удаленного "master" empty_repo.heads.master.set_tracking_branch (origin  .refs.master) # установить локальный "master" для отслеживания удаленного "masterempty_repo.heads.master.checkout () # checkout local" master "для рабочего дерева # Три вышеуказанные команды в одной: empty_repo.create_head ('master', origin.  refs.master) .set_tracking_branch (origin.refs.master) .checkout () # переименовать remotesorigin. rename ('new_origin') # push и pull ведут себя аналогично `git push | pull`origin.pull () origin.push () # assert not empty_repo.delete_remote (origin) .exists () # создание и удаление пультов 

Вы можете легко получить доступ к информации о конфигурации для удаленного, используя параметры, как если бы они были атрибутами. Однако изменение удаленной конфигурации более явное.

 assert origin.url == repo.remotes.origin.urlwith origin.config_writer как cw: cw.  set ("pushurl", "other_url") # Обратите внимание, что в python 2 запись origin.config_writer.set (...) полностью безопасна. # В py3 вызовы __del__ могут быть отложены, поэтому изменения во времени не записываются. 

Вы также можете указать настраиваемые среды для каждого вызова, используя новый диспетчер контекста в команде Git, например для использования определенного ключа SSH. Следующий пример работает с git , начиная с v2.3 :

 ssh_cmd  = 'ssh -i id_deployment_key' с repo.git.custom_environment (GIT_SSH_COMMAND = ssh_cmd): repo.remotes.origin.fetch () 

Этот устанавливает настраиваемый скрипт, который будет выполняться вместо ssh , и может использоваться в git до v2.3 :

 ssh_executable = os.path.join (rw_dir, 'my_ssh_executable.sh') с repo.git.custom_environment (GIT_SSH = ssh_executable): repo.remotes.origin  .fetch () 

Вот пример исполняемого файла, который можно использовать вместо ssh_executable выше:

 #!/bin/shID_RSA =/var/lib/openshift/5562b947ecdd5ce939000038/app-deployments/id_rsaexec/usr/bin/ssh -o StrictHostKeyChecking = no -i $ ID_RSA  "$ @" 

Обратите внимание, что сценарий должен быть исполняемым (например, chomd + x script.sh ). StrictHostKeyChecking = no используется, чтобы избежать запросов на сохранение ключа хоста в ~/.ssh/known_hosts , что происходит, если вы запускаете это как демон.

Вы также можете взглянуть на Git.update_environment (…) , если хотите настроить измененную среду на более длительный срок.

Обработка подмодулей¶

можно удобно обрабатывать с помощью методов, предоставляемых GitPython, и в качестве дополнительного преимущества GitPython обеспечивает более интеллектуальную функциональность и меньшую подверженность ошибкам чем его исходная реализация c-git, то есть GitPython изо всех сил старается сохранить целостность вашего репозитория при рекурсивном обновлении подмодулей или корректировке существующей конфигурации.

 repo = self  .roreposms = repo.submodulesassert len ​​(sms) == 1sm = sms [0] self.assertEqual (sm.name, 'gitdb') # git-python имеет gitdb как отдельный подмодуль ... self.assertEqual (sm.children (  ) [0] .name, 'smmap') # ... который имеет smmap как отдельный подмодуль # Мод  ule - это репозиторий, на который ссылается подмодуль assert sm.module_exists () # модуль доступен, чего не должно быть. assert sm. module (). working_tree_dir.endswith ('gitdb') # абсолютный путь подмодуля - это pathassert модуля sm.abspath == sm.module (). working_tree_dirself.assertEqual (len (sm.hexsha), 40) # Его sha определяет  commit to checkoutassert sm.exists () # да, этот подмодуль действителен и существует # прочитать его конфигурацию удобно assert sm.config_reader (). get_value ('path') == sm.pathself.assertEqual (len (sm.children ())  , 1) # запросить иерархию подмодулей 

В дополнение к функциям запроса вы можете переместить репозиторий подмодуля на другой путь move ( …) >, напишите его конфигурацию config_writer (). set_value (…). release () >, обновите его рабочее дерево update (…) > и удалите или добавьте их remove (…) , add (...) >.

Если вы получили объект подмодуля путем обхода объекта дерева, который не имеет корня в фиксации головы, вы должны сообщить подмодулю о его фактической фиксации, чтобы получить данные. from, используя метод set_parent_commit (...) .

Специальный тип позволяет вам рассматривать ваш главный репозиторий как корень иерархии подмодулей, что позволяет очень удобная работа с субмодулями. Его метод update (...) переопределен, чтобы предоставить расширенный способ обновления подмодулей, поскольку они меняют свои значения с течением времени. Метод обновления будет отслеживать изменения и следить за тем, чтобы ваше рабочее дерево и проверки подмодулей оставались согласованными, что очень полезно в случае удаления или добавления подмодулей для обозначения только двух обработанных случаев.

Кроме того, GitPython добавляет функциональность для отслеживания конкретной ветки, а не только фиксации. При поддержке настраиваемых методов обновления вы можете автоматически обновлять подмодули до последней версии, доступной в удаленном репозитории, а также отслеживать изменения и перемещения этих подмодулей. Чтобы использовать его, установите имя ветки, которую вы хотите отслеживать, в параметре submodule. $ Name.branch файла .gitmodules и используйте GitPython обновить методы в полученном репозитории с включенным параметром to_latest_revision . В последнем случае sha вашего подмодуля будет проигнорирован, вместо этого локальная ветвь отслеживания будет автоматически обновлена ​​до соответствующей удаленной ветки при условии отсутствия локальных изменений. Получающееся в результате поведение очень похоже на поведение svn :: externals, которое может быть полезно в разы.

Получение информации о различиях¶

Diffs обычно можно получить с помощью подклассов, поскольку они предоставляют метод diff . Эта операция дает вам возможность легко получить доступ к информации о различиях о путях.

Могут быть сделаны различия между индексом и деревьями, индексом и рабочим деревом, деревьями и деревьями, а также деревьями и рабочей копией . Если задействованы коммиты, их дерево будет использоваться неявно.

 hcommit = repo.head.commithcommit. diff () # дерево сравнения с indexhcommit.diff ('HEAD ~ 1') # дерево сравнения с предыдущим treehcommit.diff (None) # дерево сравнения с рабочим деревомindex = repo.indexindex.diff () # индекс сравнения с самим собой, что дает пустой diffindex  .diff (None) # индекс сравнения с рабочим copyindex.diff ('HEAD') # индекс сравнения с текущим деревом HEAD 

Возвращаемый элемент — это DiffIndex, который по сути, список объектов Diff. Он обеспечивает дополнительную фильтрацию, чтобы упростить поиск того, что вы, возможно, ищете.

 # Перемещение добавленных объектов Diff только для diff_added в hcommit.diff ('HEAD ~ 1'  ) .iter_change_type ('A'): print (diff_added) 

Используйте структуру diff, если вы хотите реализовать функциональность, подобную git-status.

  • Разница между индексом и деревом фиксации, на которое указывает HEAD
  • используйте repo.index.diff(repo.head.commit)
  • Различие между индексом и рабочим деревом
  • используйте repo.index.diff (None)
  • Список неотслеживаемых файлов
  • используйте repo.untracked_files

Переключение ветвей¶

Чтобы переключаться между ветвями, как в git checkout , вам необходимо указать свой HEAD sy mbolic ссылку на новую ветку и сбросьте ваш индекс и рабочую копию, чтобы они соответствовали. Простой ручной способ сделать это следующий:

 # Сбросить наше рабочее дерево 10 коммитов в прошлоеpast_branch = repo.create_head ('past_branch', '  HEAD ~ 10 ') repo.head.reference = past_branchassert not repo.head.is_detached # сбросить индекс и рабочее дерево, чтобы они соответствовали указанному commitrepo.head.reset (index = True, working_tree = True) # Чтобы отсоединить голову  , вы должны указать на фиксацию напрямую repo.head.reference = repo.commit ('HEAD ~ 5') assert repo.head.is_detached # теперь наша голова указывает 15 коммитов в прошлое, тогда как рабочее дерево # и индекс равны 10  совершает в прошлом 

Однако предыдущий подход жестоко перезаписывал бы изменения пользователя в рабочей копии и индексе и он менее сложен, чем git- оформить заказ . Последнее, как правило, не даст вам испортить вашу работу. Используйте более безопасный подход следующим образом.

 # проверить ветку с помощью git-checkout.  Это не удастся, поскольку рабочее дерево появится dirtyself.assertRaises (git.GitCommandError, repo.heads.master.checkout) repo.heads.past_branch.checkout () 

Инициализация репозитория¶

В этом примере мы инициализируем пустой репозиторий, добавим пустой файл в индекс и зафиксируем изменение.

 import gitrepo_dir = os.path.join (rw_dir, 'my-new-repo') file_name = os.path.join (repo_dir, 'new-file')  r = git.Repo. init (repo_dir) # Эта функция просто создает пустой файл ... open (file_name, 'wb'). close () r.index.add ([file_name]) r.index.commit ("initial commit") 

Обратите внимание на отдельные методы, поскольку они обычно поддерживают огромное количество аргументов для настройки своего поведения.

Использование git напрямую¶

В случае, если вам не хватает функциональности, поскольку он не был упакован, вы можете использовать команду напрямую. Он принадлежит каждому экземпляру репозитория.

 git = repo.gitgit.checkout ('HEAD', b = "my_new_branch") # создать новую веткуgit  .branch ('another-new-one') git.branch ('- D', 'another-new-one') # передавать строки для полного контроля над порядком аргументов git.for_each_ref () # '-' становится '_', когда  вызывая его 

Возвращаемое значение по умолчанию будет строкой стандартного канала вывода, созданной командой.

Аргументы ключевого слова переводят для коротких и длинных аргументов ключевого слова в командной строке. Специальное понятие git.command (flag = True) создаст флаг без значения, например command --flag .

Если в аргументах найдено None , он будет отброшен без уведомления. Списки и кортежи, переданные в качестве аргументов, будут рекурсивно распакованы в отдельные аргументы. Объекты преобразуются в строки с помощью функции str (...) .

Базы данных объектов¶

экземпляры питаются от своего экземпляра объектной базы данных, который будет использоваться при извлечении любых данных или при записи новых объектов.

Тип базы данных определяет определенные характеристики производительности, такие как количество объектов, которые могут быть прочитаны в секунду, использование ресурсов при чтении больших файлов данных, а также средний объем памяти, занимаемый вашим приложением.

GitDB¶

GitDB — это реализация объектной базы данных git на чистом Python. Это база данных по умолчанию для использования в GitPython 0.3. Он использует меньше памяти при обработке огромных файлов, но будет в 2-5 раз медленнее при извлечении большого количества небольших объектов из плотно упакованных репозиториев:

 repo =  Репо ("путь/к/репо", odbt = GitDB) 

GitCmdObjectDB¶

База данных команд git использует постоянные экземпляры файлов git-cat для чтения информации из репозитория. Они работают очень быстро при любых условиях, но потребляют дополнительную память для самого процесса. При извлечении больших файлов использование памяти будет намного выше, чем у GitDB :

 repo = Repo (  "путь/к/репо", odbt = GitCmdObjectDB) 

Отладка и настройка команды Git¶

Используя переменные среды, вы можете дополнительно настроить поведение команды git.

  • Если установлено значение, отличное от 0, все выполненные команды git будут отображаться по мере их выполнения.
  • Если установлено значение full , выполненная команда git _и_ весь ее вывод на stdout и stderr будут отображаться по мере их появления

ПРИМЕЧАНИЕ : все журналы выводятся с помощью средства ведения журнала Python, поэтому убедитесь, что ваша программа настроена для отображения сообщений уровня INFO. Если это не так, попробуйте добавить в свою программу следующее:

 import logginglogging.basicConfig (level = logging.INFO) 

  • GIT_PYTHON_GIT_EXECUTABLE
  • Если установлено, он должен содержать полный путь к исполняемому файлу git, например c: Program Files (x86) Git bin git.exe в Windows или /usr/bin/git в Linux.

И даже больше… ¶

Там больше функциональности, например, возможность для архивирования репозиториев, получения статистики и журналов, обвинений и, возможно, некоторых других вещей, которые здесь не упоминались.

Проверьте модульные тесты, чтобы получить подробное представление о том, как должна работать каждая функция б/у.

Оцените статью
techsly.ru
Добавить комментарий