first commit
David Blume

David Blume commited on 2017-02-16 22:55:09
Showing 3 changed files, with 142 additions and 0 deletions.

... ...
@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2017 David Blume
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy 
6
+of this software and associated documentation files (the "Software"), to deal 
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
9
+copies of the Software, and to permit persons to whom the Software is 
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all 
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.
... ...
@@ -0,0 +1,22 @@
1
+# same\_as\_p4\_reconcile.py
2
+
3
+This is a Python 3 script that's nearly the same as simply running "p4 reconcile".
4
+
5
+I'd already started writing it before I realized what a duplication of effort it was. So, of course, I decided to memorialize it here.
6
+
7
+To preview how a reconcile would look, issue a command like so:
8
+
9
+    same_as_p4_reconcile.py -c 48 -n source_dir/ p4_dir/
10
+
11
+Of course, issue the command with -h or --help to learn more.
12
+
13
+    same_as_p4_reconcile.py --help
14
+
15
+### Is it any good?
16
+
17
+[Yes](https://news.ycombinator.com/item?id=3067434).
18
+
19
+### Licence
20
+
21
+This software uses the [MIT license](http://git.dlma.com/same_as_p4_reconcile.git/blob/master/LICENSE.txt).
22
+
... ...
@@ -0,0 +1,99 @@
1
+#!/usr/bin/env python3
2
+#
3
+# This is really similar to "p4 reconcile", except that it
4
+# reconciles from an external directory, "source_dir", to
5
+# the Perforce repo, "perforce_dir".
6
+#
7
+# Eg., to preview the changes that would be made:
8
+#
9
+# ./same_as_p4_reconcile.py -n -c 42 source_dir perforce_dir
10
+#
11
+# To perform the actual reconciliation, drop the "-n":
12
+#
13
+# ./same_as_p4_reconcile.py -c 42 source_dir perforce_dir
14
+
15
+import os
16
+from argparse import ArgumentParser
17
+import subprocess
18
+import shutil
19
+import filecmp
20
+
21
+
22
+def call(cmd_array, changelist, preview):
23
+    """ If not preview, makes the call."""
24
+    ret = 0
25
+    if not preview:
26
+        ret = subprocess.call(add_changelist_to_cmd(cmd_array, changelist))
27
+        if ret != 0:
28
+            print("ERROR: Couldn't %s, ret = %d" % (str(cmd_array), ret))
29
+    else:
30
+        print('#', ' '.join(cmd_array))
31
+    return ret
32
+
33
+
34
+def add_changelist_to_cmd(cmd, changelist):
35
+    """Injects "-c changelist" to the p4 command."""
36
+    if changelist is None:
37
+        return cmd
38
+    cmd.insert(2, '-c')
39
+    cmd.insert(3, changelist)
40
+    return cmd
41
+
42
+
43
+def do_diff_by_name(preview, changelist, source_dir, p4_dir):
44
+    do_diff(preview, changelist, filecmp.dircmp(p4_dir, source_dir))
45
+    delete_empty_directories(p4_dir)
46
+
47
+
48
+def delete_empty_directories(top):
49
+    """ p4 deletes could leave us with empty hierarchies. """
50
+    for root, dirs, files in os.walk(top, topdown=False):
51
+        for name in dirs:
52
+            try:
53
+                os.rmdir(os.path.join(root,name))
54
+            except OSError as ex:
55
+                pass
56
+
57
+
58
+def do_diff(preview, changelist, d):
59
+    for f in d.right_only:
60
+        if os.path.isfile(os.path.join(d.right, f)):
61
+            if not preview:
62
+                shutil.copy(os.path.join(d.right, f), os.path.join(d.left, f))
63
+            call(['p4', 'add', os.path.join(d.left, f)], changelist, preview)
64
+        else:
65
+            # Can't add directory in one step. Do so by walking dir.
66
+            if not preview:
67
+                shutil.copytree(os.path.join(d.right, f), os.path.join(d.left, f))
68
+                for root, dirs, files in os.walk(os.path.join(d.left, f)):
69
+                    for f in files:
70
+                        call(['p4', 'add', os.path.join(root, f)], changelist, preview)
71
+            else:
72
+                print('# Adding hierarchy', os.path.join(d.right, f))
73
+
74
+    for f in d.left_only:
75
+        if os.path.isfile(os.path.join(d.left, f)):
76
+            call(['p4', 'delete', os.path.join(d.left, f)], changelist, preview)
77
+        else:
78
+            for root, dirs, files in os.walk(os.path.join(d.left, f)):
79
+                for f in files:
80
+                    call(['p4', 'delete', os.path.join(root, f)], changelist, preview)
81
+
82
+    for f in d.diff_files:
83
+        if not call(['p4', 'edit', os.path.join(d.left, f)], changelist, preview):
84
+            # call() returning 0 is a successful call
85
+            if not preview:
86
+                shutil.copy(os.path.join(d.right, f), os.path.join(d.left, f))
87
+
88
+    for sub in d.subdirs.values():
89
+        do_diff(preview, changelist, sub)
90
+
91
+
92
+if __name__ == '__main__':
93
+    parser = ArgumentParser(description='Similar to "p4 reconcile"')
94
+    parser.add_argument('-c', '--changelist', help='Changelist to use')
95
+    parser.add_argument('-n', '--preview', action='store_true')
96
+    parser.add_argument('source_dir', help='The newer directory outside perforce')
97
+    parser.add_argument('p4_dir', help='The perforce directory to be modified')
98
+    args = parser.parse_args()
99
+    do_diff_by_name(args.preview, args.changelist, args.source_dir, args.p4_dir)
0 100