From 682fb1fd13b99c7cf86a342be6681ddedc8a9059 Mon Sep 17 00:00:00 2001
From: Nico von Geyso <Nico.Geyso@FU-Berlin.de>
Date: Tue, 9 Oct 2012 11:07:10 +0200
Subject: [PATCH] merged create_reference and create_reference_symbolic and
 added force-Option

To be more pythonic: merged methods create_reference and create_reference_symbolic
and added force-Option. To make a new symbolic reference you have provide symbolic=True
as an additional parameter. If force=True references will although be overridden.
Otherwise an exception is raised if the references exists.

Examples:

  # normal reference
    repo.create_reference('refs/heads/foo', repo.head.hex)

  # override reference with new value
    repo.create_reference('refs/heads/foo', repo.head.hex, force=True)

  # symbolic reference
    repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic = True)
---
 include/pygit2/repository.h |  3 +-
 src/pygit2/repository.c     | 82 +++++++++++++++++++------------------
 test/test_refs.py           | 24 ++++++++---
 3 files changed, 62 insertions(+), 47 deletions(-)

diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h
index 32780e0..0f6616a 100644
--- a/include/pygit2/repository.h
+++ b/include/pygit2/repository.h
@@ -55,8 +55,7 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args);
 PyObject* Repository_create_tag(Repository *self, PyObject *args);
 PyObject* Repository_listall_references(Repository *self, PyObject *args);
 PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name);
-PyObject* Repository_create_reference(Repository *self,  PyObject *args);
-PyObject* Repository_create_symbolic_reference(Repository *self,  PyObject *args);
+PyObject* Repository_create_reference(Repository *self,  PyObject *args, PyObject* keywds);
 PyObject* Repository_packall_references(Repository *self,  PyObject *args);
 PyObject* Repository_status(Repository *self, PyObject *args);
 PyObject* Repository_status_file(Repository *self, PyObject *value);
diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c
index 8ba3704..3f31805 100644
--- a/src/pygit2/repository.c
+++ b/src/pygit2/repository.c
@@ -641,53 +641,60 @@ Repository_lookup_reference(Repository *self, PyObject *py_name)
     return wrap_reference(c_reference);
 }
 
-PyObject *
-Repository_create_reference(Repository *self,  PyObject *args)
-{
-    PyObject *py_oid;
-    git_reference *c_reference;
-    char *c_name;
-    git_oid oid;
-    int err;
-
-    /* 1- Get the C variables */
-    if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid))
-        return NULL;
-
-    err = py_str_to_git_oid_expand(self->repo, py_oid, &oid);
-    if (err < 0)
-        return Error_set(err);
-
-    /* 2- Create the reference */
-    err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0);
-    if (err < 0)
-        return Error_set(err);
-
-    /* 3- Make an instance of Reference and return it */
-    return wrap_reference(c_reference);
-}
+PyDoc_STRVAR(
+  Repository_create_reference_doc,
+  "Create a new reference \"name\" which points to a object or another reference\n\n"
+  "Arguments: (name, source, force=False, symbolic=False)\n\n"
+  "With force=True references will be overridden. Otherwise an exception is raised"
+  "You can create either a normal reference or a symbolic one:\n"
+  "  * normal reference: source has to be a valid sha hash\n"
+  "  * symbolic reference: source has to be a valid existing reference name\n\n"
+  "Examples:\n"
+  "   repo.create_reference('refs/heads/foo', repo.head.hex)\n"
+  "   repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic = True)\n"
+);
 
 PyObject *
-Repository_create_symbolic_reference(Repository *self,  PyObject *args)
+Repository_create_reference(Repository *self,  PyObject *args, PyObject* keywds)
 {
+    PyObject *py_obj;
     git_reference *c_reference;
     char *c_name, *c_target;
-    int err;
+    git_oid oid;
+    int err, symbolic = 0, force = 0;
 
-    /* 1- Get the C variables */
-    if (!PyArg_ParseTuple(args, "ss", &c_name, &c_target))
+    static char *kwlist[] = {"name", "source", "force", "symbolic", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO|ii", kwlist,
+                                     &c_name, &py_obj, &force, &symbolic))
         return NULL;
 
-    /* 2- Create the reference */
-    err = git_reference_create_symbolic(&c_reference, self->repo, c_name,
-                                        c_target, 0);
+    if(symbolic) {
+        #if PY_MAJOR_VERSION == 2
+        c_target = PyString_AsString(py_obj);
+        #else
+        c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj));
+        #endif
+        if(c_target == NULL)
+            return NULL;
+
+        err = git_reference_create_symbolic(&c_reference, self->repo, c_name,
+                                            c_target, force);
+    } else {
+        err = py_str_to_git_oid_expand(self->repo, py_obj, &oid);
+        if (err < 0)
+            return Error_set(err);
+
+        err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, force);
+    }
+
     if (err < 0)
         return Error_set(err);
 
-    /* 3- Make an instance of Reference and return it */
     return wrap_reference(c_reference);
 }
 
+
 PyObject *
 Repository_packall_references(Repository *self,  PyObject *args)
 {
@@ -827,13 +834,8 @@ PyMethodDef Repository_methods[] = {
      METH_VARARGS,
      "Create a new blob from file"},
     {"create_reference", (PyCFunction)Repository_create_reference,
-     METH_VARARGS,
-     "Create a new reference \"name\" that points to the object given by its "
-     "\"sha\"."},
-    {"create_symbolic_reference",
-      (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS,
-     "Create a new symbolic reference \"name\" that points to the reference\n"
-     "\"target\"."},
+     METH_VARARGS|METH_KEYWORDS,
+     Repository_create_reference_doc},
     {"packall_references", (PyCFunction)Repository_packall_references,
      METH_NOARGS, "Pack all the loose references in the repository."},
     {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the "
diff --git a/test/test_refs.py b/test/test_refs.py
index ec4b5cc..56e918b 100644
--- a/test/test_refs.py
+++ b/test/test_refs.py
@@ -49,8 +49,7 @@ class ReferencesTest(utils.RepoTestCase):
                          ['refs/heads/i18n', 'refs/heads/master'])
 
         # We add a symbolic reference
-        repo.create_symbolic_reference('refs/tags/version1',
-                                       'refs/heads/master')
+        repo.create_reference('refs/tags/version1','refs/heads/master', symbolic=True)
         self.assertEqual(sorted(repo.listall_references()),
                          ['refs/heads/i18n', 'refs/heads/master',
                           'refs/tags/version1'])
@@ -145,7 +144,7 @@ class ReferencesTest(utils.RepoTestCase):
     def test_reload(self):
         name = 'refs/tags/version1'
 
-        ref = self.repo.create_symbolic_reference(name, "refs/heads/master")
+        ref = self.repo.create_reference(name, "refs/heads/master", symbolic=True)
         ref2 = self.repo.lookup_reference(name)
         ref.delete()
         self.assertEqual(ref2.name, name)
@@ -176,12 +175,27 @@ class ReferencesTest(utils.RepoTestCase):
         reference = self.repo.lookup_reference('refs/tags/version1')
         self.assertEqual(reference.hex, LAST_COMMIT)
 
+        self.assertRaises(GitError, self.repo.create_reference, 
+        'refs/tags/version1', LAST_COMMIT)
+
+        reference =  self.repo.create_reference('refs/tags/version1',
+                        LAST_COMMIT, force=True)
+        self.assertEqual(reference.hex, LAST_COMMIT)
+
 
     def test_create_symbolic_reference(self):
         # We add a tag as a new symbolic reference that always points to
         # "refs/heads/master"
-        reference = self.repo.create_symbolic_reference('refs/tags/beta',
-                                                        'refs/heads/master')
+        reference = self.repo.create_reference('refs/tags/beta',
+                        'refs/heads/master', symbolic=True)
+        self.assertEqual(reference.type, GIT_REF_SYMBOLIC)
+        self.assertEqual(reference.target, 'refs/heads/master')
+
+        self.assertRaises(GitError, self.repo.create_reference, 
+        'refs/tags/beta','refs/heads/master', symbolic=True)
+
+        reference =  self.repo.create_reference('refs/tags/beta',
+                        'refs/heads/master', force=True, symbolic=True)
         self.assertEqual(reference.type, GIT_REF_SYMBOLIC)
         self.assertEqual(reference.target, 'refs/heads/master')